iOS學習系列 - 在iOS客戶端實現google oauth2登錄以及在asp.net服務端上form認證


本文介紹怎樣在iOS客戶端實現google oauth2的登錄,並且通過asp.net mvc中的controller的api接口進行form驗證。

首先,先了解下google oauth2的相關資料:

https://developers.google.com/accounts/docs/OAuth2

這里介紹了五種的登錄方式分別有:

Web Server方式(在Web上進行),Client-side方式(運行在瀏覽器中的javascript),Installed方式(例如,Android, Windows, Mac OS, iOS, Blackberry等等),Devices方式(在游戲控制台,視頻攝像機,打印機等等),Service Accounts方式(在雲服務存儲方面等等)

本文主要介紹在iOS客戶端如果在UIWebView上進行Web Server方式的oauth2登錄。

      現在必須在google上注冊一個賬戶,然后打開:https://code.google.com/apis/console/

在這里可以創建你的應用程序,例如:

 

然后需要創建一個ClientID,拖到底部有個創建按鈕:

 

這里可以看到有3個選項,分別為Web application,Service account,Installed application,這里選擇默認的Web application,完成client ID的創建。可以看到:

這里產生了Client ID以及Client secret,注意這兩項對於google的oauth2認證十分重要。

 

接下來,asp.net的服務端,采用的是Form驗證登錄的方式:

FormsAuthentication.SetAuthCookie(accountId.ToString(),  true);

MVC的Controller的API調用:

public ActionResult GoogleLogin( string error,  string code,  string state,  string mobi)

code作為google oauth2的登錄界面返回的一個code編碼,緊接着,通過client ID for web applications中的Redirect URIs,自動跳轉到GoogleLogin接口,送到服務端,在服務端進行進行code驗證,得到token的相關賬號

//根據google回調獲取對應google賬號

var grant =  this.GetGoogleGrantByCode(code);

private  string GetGoogleGrantByCode( string code)
        {
             using ( var wc =  new WebClient())
            {
                wc.Headers.Add( " Content-Type "" application/x-www-form-urlencoded ");
                 // 登錄后根據code獲取token
                 var data =  string.Format( " client_id={0}&client_secret={1}&redirect_uri={2}&code={3}&grant_type=authorization_code "
                    ,  this._googleClientId
                    ,  this._googleClientSecret
                    , HttpUtility.UrlEncode( this.GetGoogleRedirectUrl())
                    , code);
                 if ( this._log.IsDebugEnabled)
                     this._log.Debug(data);
                 try
                {
                     return Encoding.UTF8.GetString(wc.UploadData( this._googleOAuth2TokenUrl
                 ,  " POST "
                        , Encoding.UTF8.GetBytes(data)));
                }
                 catch (WebException e)
                {
                     if (e.Response ==  null)
                         throw e;
                     using ( var reader =  new StreamReader((e  as WebException).Response.GetResponseStream()))
                         throw  new Exception(reader.ReadToEnd());
                }
            }
        }

其中,_googleClienId和_googleClientSecret就是剛才在google code console中產生的兩項,GetGoogleRedirctUrl()取的也是google code console中設置的Redirect URIs;_googleOAuth2TokenUrl為https://accounts.google.com/o/oauth2/token

通過POST請求,如果順利的話,就可以得到一些Token信息,其中包括accessToken,refreshToken以及過期的日期等等。通過accessToken以及userInfo的api接口,就可以得到當前google賬戶的email信息。

using ( var wc =  new WebClient() { Encoding = Encoding.UTF8 })
                 return _serializer.JsonDeserialize<IDictionary< stringstring>>(
                    wc.DownloadString( this._googleOAuth2UserUrl +  " ?access_token= " + access_token))[ " email "];

其中_googleOAuth2UserUrl為https://www.googleapis.com/oauth2/v1/userinfo

 

接下來,我們看下iOS客戶端怎樣進行google oauth2登錄,在http://code.google.com/p/gtm-oauth2/通過svn獲取code source:

打開gtm-oauth2-read-only/source,把里面的相關的obj-c的文件拷貝出來,例如:

現在,就可以在你的應用程序中使用它了:

創建一個LoginViewController.h/m,並引用頭文件:

#import  " GTMOAuth2Authentication.h "
#import  " GTMOAuth2ViewControllerTouch.h "

界面如下:

點擊"使用谷歌賬號登錄“按鈕,進入google登錄頁面:

- ( void)googleLogin:( id)sender
{
    NSLog( @" 進入google oauth登錄頁面 ");
 
    [self signOut:nil];
 
    NSString *scope = [[[SysConfig instance] keyValue] objectForKey: @" googleScope "];
 
    NSString *keychainItemName  = kKeychainItemName;
 
    SEL finishedSel = @selector(viewController:finishedWithAuth:error:);
 
    GTMOAuth2ViewControllerTouch *viewController;
    viewController = [GTMOAuth2ViewControllerTouch controllerWithScope:scope
                                                              clientID:googleClientId
                                                          clientSecret:googleClientSecret
                                                      keychainItemName:keychainItemName
                                                               delegate:self
                                                      finishedSelector:finishedSel];
    viewController.loginDelegate = self;
 
    NSString *html =  @" <html><body bgcolor=white><div align=center>正在進入google登錄頁面...</div></body></html> ";
    viewController.initialHTMLString = html;
 
    [self.navigationController pushViewController:viewController animated:YES];
}

其中googleScope可以設置一個授權google api應用的范圍,GTMOAuth2ViewControllerTouch


實際上,GTMOAuth2ViewControllerTouch進入一個擁有UIWebView的嵌套頁面,initialHtmlString可進行UIWebView的初始化,並且通過UIWebViewDelegate的委托可以來獲取相關的NSURLRequest的信息。接着,這里也要填寫剛才對應的googleClientId以及googleClientSecret

現在在這里填寫你的google賬號,點擊"sign in",進入下一步;

另外,修改下GTMOAuth2SignIn.m里面的代碼:

+ (NSString *)nativeClientRedirectURI {
   // return kOOBString;
   return [[[SysConfig instance] keyValue] objectForKey: @" google_redirect_uri "];

從我的Config.plist文件中去讀取google_redirect_uri

在parametersForWebRequest方法中進行調整:

NSMutableDictionary *paramsDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                      @" code "@" response_type ",
                                     clientID,  @" client_id ",
                                      @" joke "@" state ",
                                      @" true "@" mobi ",
                                      @" force ", @" approval_prompt ",
                                      @" offline "@" access_type ",
                                     scope,  @" scope "//  scope may be nil
                                     nil];

讓paramsDict接受一個state為"joke"的參數,joke的目的是為了在第一次跳轉中不對code進行處理,如果code被執行oauth2后,就已經失效。 

對應的服務端API,在GoogleLogin加上:

public ActionResult GoogleLogin( string error,  string code,  string state,  string mobi)
        {
             if (state ==  " joke ")
                 return Json( false, JsonRequestBehavior.AllowGet);

先返回方法,這樣我就可以在GTMOAuth2ViewControllerTouch.m的webViewDidFinishLoad:方法中,獲取到原先code的值:

- ( void)webViewDidFinishLoad:(UIWebView *)webView {
    [self notifyWithName:kGTMOAuth2WebViewStoppedLoading
                 webView:webView
                    kind:kGTMOAuth2WebViewFinished];
 
    NSString *query = webView.request.URL.query;
    NSArray *array = [query componentsSeparatedByString: @" & "];
     if(array.count ==  2)
    {
        [self popView];
        [loginDelegate googleLoginFinish:array];
    }
 
    NSString *title = [webView stringByEvaluatingJavaScriptFromString: @" document.title "];
     if ([title length] >  0) {
        [signIn_ titleChanged:title];
    }  else {
#if DEBUG
         //  Verify that Javascript is enabled
        NSString *result = [webView stringByEvaluatingJavaScriptFromString: @" 1+1 "];
        NSAssert([result integerValue] ==  2@" GTMOAuth2: Javascript is required ");
#endif
    }
 
    [signIn_ cookiesChanged:[NSHTTPCookieStorage sharedHTTPCookieStorage]];
 
    [self updateUI]; 

這里用到一個loginDelegate的委托,會把當前的array返回給之前的LoginViewController中:

- ( void)googleLoginFinish:(NSArray*)array
{
    NSString *codeQuery = [array objectAtIndex: 1];
    NSArray *patams = [codeQuery componentsSeparatedByString: @" = "];
    NSString *code = [patams objectAtIndex: 0];
     if([code isEqualToString: @" code "])
    {
        self.HUD = [Tools process: @" 登錄中 " view:self.view];
        NSMutableDictionary *context = [NSMutableDictionary dictionary];
        [context setObject: @" GOOGLELOGIN " forKey:REQUEST_TYPE];
 
        NSString *anthCode = [patams objectAtIndex: 1];
        [AccountService googleLogin: @"" code:anthCode state: @" login " mobi: @" true " joke: @" false " context:context  delegate:self];
    }
}

AccountService的googleLogin就會去調用服務端的API了,並且完成回調:

- ( void)requestFinished:(ASIHTTPRequest *)request
{
NSDictionary *userInfo = [request userInfo];
    NSString * requestType = [userInfo objectForKey:REQUEST_TYPE];
if([requestType isEqualToString: @" GOOGLELOGIN "])
    {
         if(request.responseStatusCode ==  200)
        {
            NSArray* array = request.responseCookies;
            NSLog( @" Cookies的數組個數: %d ",  array.count);
 
            NSDictionary *dict = [NSHTTPCookie requestHeaderFieldsWithCookies:array];
            NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:dict];
            NSHTTPCookieStorage *sharedHTTPCookie = [NSHTTPCookieStorage sharedHTTPCookieStorage];
 
            [sharedHTTPCookie setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
            [sharedHTTPCookie setCookie:cookie];
            NSString *username = [request.responseString stringByReplacingOccurrencesOfString: @" \"" withString:@""];
            [[ConstantClass instance] setUsername:username];
            [[ConstantClass instance] setLoginType:@
"google " ];
            [ConstantClass saveToCache];
 
            [self dismissModalViewControllerAnimated:NO];
            [ delegate loginFinish];
        }
         else
        {
            [Tools failed:self.HUD];
        }
    }
}

其中,本地存儲了一份從服務端接收到Cookie存放在本地,已備刷新服務端的Form驗證的過期。

 

服務端API最終調用代碼:

public ActionResult GoogleLogin( string error,  string code,  string state,  string mobi)
       {
             if (state ==  " joke ")
                 return Json( false, JsonRequestBehavior.AllowGet);
// 根據google回調獲取對應google賬號
             var grant =  this.GetGoogleGrantByCode(code);
             var dict = _serializer.JsonDeserialize<IDictionary< stringstring>>(grant);
             var email =  this.GetGoogleAccount(dict[ " access_token "]);
this.SetLogin(email, grant);
             return Json(email, JsonRequestBehavior.AllowGet);
}


這樣就完成了整個google oauth2登錄以及在服務端的Form認證。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM