sign in with Apple和facebook登錄集成

sign in with Apple 接入

1.項(xiàng)目配置

  • 需要配置具備sign in with Apple功能的profile文件
  • xcode項(xiàng)目的 signing and capabilities中需要新增sign in with apple模塊

2.代碼接入

1. 創(chuàng)建登錄按鈕

       // 使用系統(tǒng)提供的按鈕,要注意不支持系統(tǒng)版本的處理
   if (@available(iOS 13.0, *)) {
       ASAuthorizationAppleIDButton *appleIDBtn = [ASAuthorizationAppleIDButton buttonWithType:ASAuthorizationAppleIDButtonTypeDefault style:ASAuthorizationAppleIDButtonStyleWhite];
       appleIDBtn.frame = CGRectMake(30, self.view.bounds.size.height - 180,   self.view.bounds.size.width - 60, 100);
       [appleIDBtn addTarget:self action:@selector(didAppleIDBtnClicked)       forControlEvents:UIControlEventTouchUpInside];
   }

這里的sign in with apple按鈕有兩種文本,三種樣式:

   typedef NS_ENUM(NSInteger, ASAuthorizationAppleIDButtonType) {
   ASAuthorizationAppleIDButtonTypeSignIn,
   ASAuthorizationAppleIDButtonTypeContinue,

   ASAuthorizationAppleIDButtonTypeSignUp API_AVAILABLE(ios(13.2),     macos(10.15.1), tvos(13.1)) API_UNAVAILABLE(watchos),

   ASAuthorizationAppleIDButtonTypeDefault =   ASAuthorizationAppleIDButtonTypeSignIn,
   } NS_SWIFT_NAME(ASAuthorizationAppleIDButton.ButtonType)    API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0))  API_UNAVAILABLE(watchos);


   typedef NS_ENUM(NSInteger, ASAuthorizationAppleIDButtonStyle) {
   ASAuthorizationAppleIDButtonStyleWhite,
   ASAuthorizationAppleIDButtonStyleWhiteOutline,
   ASAuthorizationAppleIDButtonStyleBlack,
   } NS_SWIFT_NAME(ASAuthorizationAppleIDButton.Style)     API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0))  API_UNAVAILABLE(watchos);
   

2. 監(jiān)聽(tīng)按鈕點(diǎn)擊事件,發(fā)送登錄請(qǐng)求

if (@available(iOS 13.0, *)) {
        // 基于用戶的Apple ID授權(quán)用戶,生成用戶授權(quán)請(qǐng)求的一種機(jī)制
        ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init];
        // 創(chuàng)建新的AppleID 授權(quán)請(qǐng)求
        ASAuthorizationAppleIDRequest *appleIDRequest = [appleIDProvider createRequest];
        // 在用戶授權(quán)期間請(qǐng)求的聯(lián)系信息
        appleIDRequest.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
        // 由ASAuthorizationAppleIDProvider創(chuàng)建的授權(quán)請(qǐng)求 管理授權(quán)請(qǐng)求的控制器
        ASAuthorizationController *authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[appleIDRequest]];
        // 設(shè)置授權(quán)控制器通知授權(quán)請(qǐng)求的成功與失敗的代理
        authorizationController.delegate = self;
        // 設(shè)置提供 展示上下文的代理,在這個(gè)上下文中 系統(tǒng)可以展示授權(quán)界面給用戶
        authorizationController.presentationContextProvider = self;
        // 開(kāi)始登錄請(qǐng)求
        [authorizationController performRequests];
    }else{
        // 處理不支持系統(tǒng)版本
        NSLog(@"該系統(tǒng)版本不可用Apple登錄");
    }
// 如果存在iCloud Keychain 憑證或者AppleID 憑證提示用戶
- (void)perfomExistingAccountSetupFlows{
    
    if (@available(iOS 13.0, *)) {
        // 基于用戶的Apple ID授權(quán)用戶,生成用戶授權(quán)請(qǐng)求的一種機(jī)制
        ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init];
        // 授權(quán)請(qǐng)求AppleID
        ASAuthorizationAppleIDRequest *appleIDRequest = [appleIDProvider createRequest];
        // 為了執(zhí)行鑰匙串憑證分享生成請(qǐng)求的一種機(jī)制
        ASAuthorizationPasswordProvider *passwordProvider = [[ASAuthorizationPasswordProvider alloc] init];
        ASAuthorizationPasswordRequest *passwordRequest = [passwordProvider createRequest];
        // 由ASAuthorizationAppleIDProvider創(chuàng)建的授權(quán)請(qǐng)求 管理授權(quán)請(qǐng)求的控制器
        ASAuthorizationController *authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[appleIDRequest, passwordRequest]];
        // 設(shè)置授權(quán)控制器通知授權(quán)請(qǐng)求的成功與失敗的代理
        authorizationController.delegate = self;
        // 設(shè)置提供 展示上下文的代理,在這個(gè)上下文中 系統(tǒng)可以展示授權(quán)界面給用戶
        authorizationController.presentationContextProvider = self;
        // 在控制器初始化期間啟動(dòng)授權(quán)流
        [authorizationController performRequests];
    }else{
        // 處理不支持系統(tǒng)版本
        NSLog(@"該系統(tǒng)版本不可用Apple登錄");
    }
}

3. 通過(guò)代理方法監(jiān)聽(tīng)回調(diào)結(jié)果

  • 授權(quán)成功
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)){
 
 if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
     // 用戶登錄使用ASAuthorizationAppleIDCredential
     ASAuthorizationAppleIDCredential *appleIDCredential = authorization.credential;
     NSString *user = appleIDCredential.user;
     
     if (user) {
         [YostarKeychain save:KEYCHAIN_IDENTIFIER(@"userIdentifier") data:user];
     }
     // 使用過(guò)授權(quán)的,可能獲取不到以下三個(gè)參數(shù)
     NSString *familyName = appleIDCredential.fullName.familyName;
     NSString *givenName = appleIDCredential.fullName.givenName;
     NSString *email = appleIDCredential.email;
     
     NSData *identityToken = appleIDCredential.identityToken;
     NSData *authorizationCode = appleIDCredential.authorizationCode;
     
     // 服務(wù)器驗(yàn)證需要使用的參數(shù)
     NSString *identityTokenStr = [[NSString alloc] initWithData:identityToken encoding:NSUTF8StringEncoding];
     NSString *authorizationCodeStr = [[NSString alloc] initWithData:authorizationCode encoding:NSUTF8StringEncoding];
     NSLog(@"%@\n\n%@", identityTokenStr, authorizationCodeStr);
     
     // Create an account in your system.
     // For the purpose of this demo app, store the userIdentifier in the keychain.
     
 }
}

sign in with Apple授權(quán)成功后,可以拿到以下數(shù)據(jù):

 > 1.userId :用戶的唯一標(biāo)識(shí)符,一個(gè)apple ID對(duì)應(yīng)一個(gè)唯一的userId,通過(guò)這個(gè)id可以  在后臺(tái)用作數(shù)據(jù)綁定

> 2.Verification data:后臺(tái)用來(lái)用作身份驗(yàn)證的數(shù)據(jù),具體驗(yàn)證流程參考:https://www.yuque.com/zhanglong/bb0s5d/cxbh7n

> 3.用戶名和郵箱數(shù)據(jù)
  • 授權(quán)失敗
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0)){
 // Handle error.
 NSLog(@"Handle error:%@", error);
 NSString *errorMsg = nil;
 switch (error.code) {
     case ASAuthorizationErrorCanceled:
         errorMsg = @"用戶取消了授權(quán)請(qǐng)求";
         break;
     case ASAuthorizationErrorFailed:
         errorMsg = @"授權(quán)請(qǐng)求失敗";
         break;
     case ASAuthorizationErrorInvalidResponse:
         errorMsg = @"授權(quán)請(qǐng)求響應(yīng)無(wú)效";
         break;
     case ASAuthorizationErrorNotHandled:
         errorMsg = @"未能處理授權(quán)請(qǐng)求";
         break;
     case ASAuthorizationErrorUnknown:
         errorMsg = @"授權(quán)請(qǐng)求失敗未知原因";
         break;
         
     default:
         break;
 }
}

4.當(dāng)用戶終止app使用sign in with apple功能或者在設(shè)置中注銷(xiāo)apple ID時(shí),需要做退出登錄處理,此時(shí)需要對(duì)此行為做監(jiān)聽(tīng),具體是在進(jìn)入應(yīng)用和后臺(tái)返回前臺(tái)時(shí),需要用到userID去查詢此時(shí)賬戶的登錄狀態(tài),具體代碼如下:

- (void)appleLoginStatueChange{
    
    if (@available(iOS 13.0, *)) {
            // A mechanism for generating requests to authenticate users based on their Apple ID.
            // 基于用戶的Apple ID 生成授權(quán)用戶請(qǐng)求的機(jī)制
            ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new];
            // 注意 存儲(chǔ)用戶標(biāo)識(shí)信息需要使用鑰匙串來(lái)存儲(chǔ) 這里筆者簡(jiǎn)單期間 使用NSUserDefaults 做的簡(jiǎn)單示例
            NSString *userIdentifier = [YostarKeychain load:KEYCHAIN_IDENTIFIER(@"userIdentifier")];
            NSLog(@"observeAuthticationState----:%@",userIdentifier);
            if (userIdentifier) {
                NSString* __block errorMsg = nil;
                //Returns the credential state for the given user in a completion handler.
                // 在回調(diào)中返回用戶的授權(quán)狀態(tài)
                [appleIDProvider getCredentialStateForUserID:userIdentifier completion:^(ASAuthorizationAppleIDProviderCredentialState credentialState, NSError * _Nullable error) {
                    switch (credentialState) {
                            // 蘋(píng)果證書(shū)的授權(quán)狀態(tài)
                        case ASAuthorizationAppleIDProviderCredentialRevoked:
          // 蘋(píng)果授權(quán)憑證失效 -> 此時(shí)是用戶在設(shè)置中終止了此App的sign in with Apple功能
                            errorMsg = @"蘋(píng)果授權(quán)憑證失效";
                            // 需要重新登錄
                            break;
                        case ASAuthorizationAppleIDProviderCredentialAuthorized:
                            // 蘋(píng)果授權(quán)憑證狀態(tài)良好
                            errorMsg = @"蘋(píng)果授權(quán)憑證狀態(tài)良好";
                            break;
                        case ASAuthorizationAppleIDProviderCredentialNotFound:
                            // 未發(fā)現(xiàn)蘋(píng)果授權(quán)憑證 -> 此時(shí)是注銷(xiāo)apple ID的時(shí)候
                            // 需要重新登錄
                            errorMsg = @"未發(fā)現(xiàn)蘋(píng)果授權(quán)憑證";
                            break;
                            // 可以引導(dǎo)用戶重新登錄
                        case ASAuthorizationAppleIDProviderCredentialTransferred:
                            errorMsg = @"蘋(píng)果授權(quán)信息變動(dòng)";
                            break;
                    }
                    dispatch_async(dispatch_get_main_queue(), ^{
                        NSLog(@"SignInWithApple授權(quán)狀態(tài)變化情況");
                        NSLog(@"%@", errorMsg);
                    });
            }];
                
        }
    }
}

5.后臺(tái)驗(yàn)證

**主要流程是客戶端拿到userId, identityToken, authorizationCode后傳給后臺(tái),后臺(tái)拼接參數(shù)調(diào)用接口:https://appleid.apple.com/auth/token去蘋(píng)果服務(wù)器請(qǐng)求認(rèn)證。

1. 參數(shù)的生成

client_id: 為app的 bundle identifier

code:為手機(jī)端獲取到的 authorizationCode 信息

grant_type:傳入固定字符串 authorization_code

client_secret:需要自己我們通過(guò)私鑰生成

2.client_secret的生成

1.登錄開(kāi)發(fā)者后臺(tái),選中keys,創(chuàng)建 privateKey,獲取到 Key ID 和 私鑰,創(chuàng)建完之后把私鑰下載下來(lái),并保存好,注意,私鑰只能下載一次

2.拿到上面所有信息之后,可以通過(guò)如下代碼生成 client_secret ,代碼為 Ruby 代碼,確保已安裝ruby環(huán)境。

require "jwt"

key_file = "Path to the private key"
team_id = "Your Team ID"
client_id = "Your App Bundle ID"
key_id = "The Key ID of the private key"
validity_period = 180 # In days. Max 180 (6 months) according to Apple docs.

private_key = OpenSSL::PKey::EC.new IO.read key_file

token = JWT.encode(
  {
    iss: team_id,
    iat: Time.now.to_i,
    exp: Time.now.to_i + 86400 * validity_period,
    aud: "https://appleid.apple.com",
    sub: client_id
  },
  private_key,
  "ES256",
  header_fields=
  {
    kid: key_id 
  }
)
puts token

3.創(chuàng)建文件 secret_gen.rb ,把上面代碼粘貼進(jìn)去,執(zhí)行 ruby secret_gen.rb 即可生成 client_secret
代碼中這個(gè) key_file 需要指定剛才下載的文件的地址

3.調(diào)用接口:https://appleid.apple.com/auth/token
返回?cái)?shù)據(jù)如下:

{

"access_token": "一個(gè)token,此處省略",

"token_type": "Bearer",

"expires_in": 3600,

"refresh_token": "一個(gè)token,此處省略",

"id_token": "結(jié)果是JWT,字符串形式,此處省略"

}
服務(wù)器拿到相應(yīng)結(jié)果,其中 id_token 也是 JWT 數(shù)據(jù),decode 出 payload 部分如下

{
  "iss": "https://appleid.apple.com",
  "aud": "這個(gè)對(duì)應(yīng)app的bundleid",
  "exp": 1567494694,
  "iat": 1567494094,
  "sub": "這個(gè)字段和手機(jī)端獲取的user信息相同",
  "at_hash": "nRYP2wGXBGT0bIYWibx4Yg",
  "auth_time": 1567494094
}

其中 aud 部分與你的app的bundleID一致, sub 部分即與手機(jī)端獲得的 user 一致,服務(wù)器端通過(guò)對(duì)比 sub 字段信息是否與手機(jī)端上傳的 user 信息一致來(lái)確定是否成功登錄.

更詳細(xì)的接入信息請(qǐng)參考:

https://www.yuque.com/zhanglong/bb0s5d/cxbh7n

http://www.itdecent.cn/p/e1284bd8c72a

facebook登錄接入(SDK6.2.0)

1.首先進(jìn)入facebook開(kāi)發(fā)者官方網(wǎng)站

facebook開(kāi)發(fā)者官網(wǎng):https://developers.facebook.com/?no_redirect=1

并且成為其開(kāi)發(fā)者。

2.創(chuàng)建應(yīng)用,獲取應(yīng)用編號(hào)

3.進(jìn)入facebook集成登錄模塊

facebook登錄集成文檔:https://developers.facebook.com/docs/facebook-login/ios?sdk=fbsdk

  • 1.選擇需要應(yīng)用
  • 2.選擇集成方式,此處建議使用cocoapods方式集成,官方提供下載的SDK缺少Bolts庫(kù),會(huì)導(dǎo)致集成錯(cuò)誤
  • 3.在 Facebook 注冊(cè)和配置您的應(yīng)用,配置bundle ID
  • 4.配置工程項(xiàng)目,在xocde項(xiàng)目的info.plist中配置:
<key>CFBundleURLTypes</key> 
<array> 
<dict> 
<key>CFBundleURLSchemes</key> 
<array> <string>fb2516056732057358</string> </array> 
</dict> 
</array> 
<key>FacebookAppID</key> 
<string>2516056732057358</string> 
<key>FacebookDisplayName</key> 
<string>changeDemo</string>
 <key>LSApplicationQueriesSchemes</key> 
 <array>
 <string>fbapi</string> 
 <string>fbapi20130214</string>
 <string>fbapi20130410</string>
 <string>fbapi20130702</string> 
 <string>fbapi20131010</string> 
 <string>fbapi20131219</string> 
 <string>fbapi20140410</string> 
 <string>fbapi20140116</string> 
 <string>fbapi20150313</string> 
 <string>fbapi20150629</string> 
 <string>fbapi20160328</string>
 <string>fbauth</string> 
 <string>fb-messenger-share-api</string>
 <string>fbauth2</string> 
 <string>fbshareextension</string>
 </array>
  • 5.在appdelegate.m文件中導(dǎo)入:#import < FBSDKCoreKit/FBSDKCoreKit.h>,此時(shí)不考慮SceneDelegate存在的情況.
 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 
    [[ApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions]; 
    // Add any custom logic here. 
    return YES;
 } 
 
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
     BOOL handled = [[ApplicationDelegate sharedInstance] application:application openURL:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] annotation:options[UIApplicationOpenURLOptionsAnnotationKey] ]; 
     // Add any custom logic here. 
     return handled;
}

如果應(yīng)用中存在SceneDelegate,那么還需配置一下代碼:

- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts { 
    UIOpenURLContext *openURLContext = URLContexts.allObjects.firstObject; 
    if (openURLContext) { 
         [[ApplicationDelegate sharedInstance] application:UIApplication.sharedApplication  openURL:openURLContext.URL sourceApplication:openURLContext.options.sourceApplication annotation:openURLContext.options.annotation]; 
    } 
    // Add any custom logic here. 
}
    
  • 6.將facebook登錄代碼接入到app中:

    • 使用facebook定制化FBSDKLoginButton按鈕登錄,此按鈕會(huì)根據(jù)用戶登錄和登出自動(dòng)更換文本樣式
     FBSDKLoginButton *loginButton = [[FBSDKLoginButton alloc] init];
         // Optional: Place the button in the center of your view.
      loginButton.center = self.view.center;
     loginButton.permissions = @[@"public_profile", @"email"];
     loginButton.delegate = self;
         [self.view addSubview:loginButton];
    
     通過(guò)FBSDKLoginButtonDelegate代理監(jiān)聽(tīng)登錄的狀態(tài):
    
    - (void)loginButton:(FBSDKLoginButton *)loginButton
    didCompleteWithResult:(nullable FBSDKLoginManagerLoginResult *)result
              error:(nullable NSError *)error{
         if (result.token) {
                NSLog(@"登錄成功,userID = %@",result.token.userID);
        }else{
                NSLog(@"登錄失敗");
            }
    }
    
    - (void)loginButtonDidLogOut:(FBSDKLoginButton *)loginButton{
            NSLog(@"---退出登錄");
    }
    
    • 使用FBSDKLoginManager登錄
    [self.loginManager logInWithPermissions:@[@"public_profile", @"email"]  fromViewController:self handler:^(FBSDKLoginManagerLoginResult * _Nullable result,  NSError * _Nullable error) {
        if (result.token) {
                NSLog(@"登錄成功,userID = %@",result.token.userID);
        }else{
                 NSLog(@"登錄失敗");
        }
    }];
    
        // 退出登錄
        [self.loginManager logOut];
    
    • 判斷當(dāng)前用戶是否登錄:
    if ([FBSDKAccessToken currentAccessToken]) {
     // User is logged in, do work such as go to next view controller. 
    }
    

facebook登錄成功,最終會(huì)獲取到FBSDKAccessToken類(lèi)型的模型,如下:

@property (class, nonatomic, copy, nullable) FBSDKAccessToken *currentAccessToken;

@property (nonatomic, copy, readonly) NSDate *expirationDate;

@property (nonatomic, copy, readonly) NSDate *refreshDate;

// 身份驗(yàn)證的令牌
@property (nonatomic, copy, readonly) NSString *tokenString;

// 用戶唯一id
@property (nonatomic, copy, readonly) NSString *userID;

這里我們需要傳給后臺(tái)的就是,userID和tokenString

經(jīng)測(cè)試發(fā)現(xiàn)最新的facebook不支持facebook App授權(quán)登錄,僅支持應(yīng)用內(nèi)跳轉(zhuǎn)到網(wǎng)頁(yè)登錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容