ios-苹果登录


title: ios-苹果登录
categories: Ios
tags: [ios, signin, 登录]
date: 2021-05-10 17:52:14
comments: false
mathjax: true
toc: true

ios-苹果登录


前篇

  • 官方
    • Authentication Services - https://developer.apple.com/documentation/authenticationservices
    • Generate and Validate Tokens - https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
  • iOS 苹果授权登录(Sign in with Apple) - https://www.wangquanwei.com/560.html
  • 苹果推出了 Sign in with Apple 功能 - https://www.cnblogs.com/ljcgood66/p/12537389.html

客户端接入

  1. singing & capabilities 中加入 sign in with apple (不勾选会出现 认证错误

  2. 登录代码

    1. 实现回调 ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding

      @interface AppleHelper()<ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding>
      @end
      
      @implementation AppleHelper
      // ------------------------------- 实现 ASAuthorizationControllerDelegate 的接口
      - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization  API_AVAILABLE(ios(13.0)){
      
          if (@available(iOS 13.0, *)) {
              if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
                  // 用户登录使用 ASAuthorizationAppleIDCredential
                  ASAuthorizationAppleIDCredential *appleIDCredential = authorization.credential;
                  
                  NSString *userId = appleIDCredential.user;
                  NSString *familyName = appleIDCredential.fullName.familyName;
                  NSString *givenName = appleIDCredential.fullName.givenName;
                  NSString *email = appleIDCredential.email;
                  NSString* extB = [NSString stringWithFormat:@"%s|%s|%s", email, familyName, givenName];
       
                  // 服务器验证需要使用的参数
                  NSString *identityTokenStr = [[NSString alloc] initWithData:appleIDCredential.identityToken encoding:NSUTF8StringEncoding];
                  NSString *authorizationCodeStr = [[NSString alloc] initWithData:appleIDCredential.authorizationCode encoding:NSUTF8StringEncoding];
                  
                  CThirdLogin* tl = [CThirdLogin new];
                  tl.userId = userId;
                  tl.token = identityTokenStr;
                  tl.extA = authorizationCodeStr;
                  tl.extB = extB;
                  [self doCallback:ECodeOk msg:[tl yy_modelToJSONString]];
              } else if ([authorization.credential isKindOfClass:[ASPasswordCredential class]]) {
                  // 用户登录使用现有的密码凭证
                  ASPasswordCredential *passwordCredential = authorization.credential;
                  
                  NSString *userId = passwordCredential.user;
                  NSString *password = passwordCredential.password;
                  
                  CThirdLogin* tl = [CThirdLogin new];
                  tl.userId = userId;
                  tl.token = password;
                  [self doCallback:ECodeOk msg:[tl yy_modelToJSONString]];
              } else {
                  [self doCallback:ECodeUnknown msg:@"--- didCompleteWithAuthorization"];
              }
          } else {
              [self doCallback:ECodeSupportError msg:@"--- didCompleteWithAuthorization"];
          }
      }
      
      - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error  API_AVAILABLE(ios(13.0)){
          [self doCallback:ECodeLoginError msg:[NSString stringWithFormat:@"%@", error]];
      }
      
      // ------------------------------- 实现 ASAuthorizationControllerPresentationContextProviding 的接口
      - (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller  API_AVAILABLE(ios(13.0)){
          return [Tool getRootViewCtrl].view.window;
      }
      
      @end
      
      • 登录返回的数据

        2021-05-10 18:04:09.904086+0800 MyIosTest[271:4447] --- userId: 001104.1f7794b73e074767bd31e4173fc5fd8e.1004
        2021-05-10 18:04:09.904172+0800 MyIosTest[271:4447] --- familyName: Mehak, givenName: Shivani, nickname: (null)
        2021-05-10 18:04:09.904208+0800 MyIosTest[271:4447] --- email: htj2n7ks5z@privaterelay.appleid.com
        2021-05-10 18:04:09.904247+0800 MyIosTest[271:4447] --- identityTokenStr: eyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLnJtZ3J1bW15aW9zLnBybyIsImV4cCI6MTYyMDcyNzQ0OSwiaWF0IjoxNjIwNjQxMDQ5LCJzdWIiOiIwMDExMDQuMWY3Nzk0YjczZTA3NDc2N2JkMzFlNDE3M2ZjNWZkOGUuMTAwNCIsImNfaGFzaCI6IjZEQmNwakNhbGZFXy1GS2o5Q3diaGciLCJlbWFpbCI6Imh0ajJuN2tzNXpAcHJpdmF0ZXJlbGF5LmFwcGxlaWQuY29tIiwiZW1haWxfdmVyaWZpZWQiOiJ0cnVlIiwiaXNfcHJpdmF0ZV9lbWFpbCI6InRydWUiLCJhdXRoX3RpbWUiOjE2MjA2NDEwNDksIm5vbmNlX3N1cHBvcnRlZCI6dHJ1ZX0.1IQXo9mo9OPI5TSf0gLuXj1YTKETFBBGZtCgQD9GKTTKHztj9nhjHHBCziDLl__TKc4JSxAl5mUZO-nhrnsuU5Q8dGAbgQwDle5uGeBQ8NTw00jSr-JpjVOZ-MuMnWR1wygzgQGyQV20fGpQ9g37U6KsXVGoPg32KfluEZmVygUzD8IOy27NolP3XtC4FwYn7IUDDseeTN1_8tCbf3PUOdm8jHzVN08asGzP_jmJVrPZV-Dr3wCDoTgwDfkeVBr8_pdF3-tq955TznRy_voiaw56x-V-ZNF3OUN826C2D9wtg9pY4cTV8jxj2YGoIfZgPx-dX-iVXgkT0E4DP6vs5A
        2021-05-10 18:04:09.905094+0800 MyIosTest[271:4447] --- authorizationCodeStr: c8128abb30c714a7aafc9a1d55e702fac.0.mrrqu.jZtUOrps6JJpugTzrUU-Bw
        
        
    2. 未登录过的情况下

      -(void)login:(CodeIdFn)cb {
          self.cb = cb;
          
          if (@available(iOS 13.0, *)) {
              ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new];
              ASAuthorizationAppleIDRequest *request = appleIDProvider.createRequest;
              request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
              ASAuthorizationController *controller = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
              controller.delegate = self;
              controller.presentationContextProvider = self;
              [controller performRequests];
          } else {
              [self doCallback:ECodeSupportError msg:@"--- login"];
          }
      }
      
    3. 已登录过的情况下

      // 如果存在iCloud Keychain 凭证或者AppleID 凭证提示用户
      -(void)perfomExistingAccount:(CodeIdFn)cb {
          self.cb = cb;
          
          if (@available(iOS 13.0, *)) {
              ASAuthorizationAppleIDRequest *appleIDRequest = [[ASAuthorizationAppleIDProvider new] createRequest];
              ASAuthorizationPasswordRequest *passwordRequest = [[ASAuthorizationPasswordProvider new] createRequest];
              ASAuthorizationController *controller = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[appleIDRequest, passwordRequest]];
              controller.delegate = self;
              controller.presentationContextProvider = self;
              [controller performRequests];
          } else {
             [self doCallback:ECodeSupportError msg:@"--- perfomExistingAccount"];
         }
      }
      
      
    4. 检测已存在的 userId 是否合法

      // 使用 userId 查看是否有合法的登录用户
      -(void)checkAuth:(NSString*)userId completeHandler:(BoolFn)completeHandler {
          if (userId == nil || userId.length <= 0) {
              completeHandler(NO);
              return;
          }
      
          if (@available(iOS 13.0, *)) {
              ASAuthorizationAppleIDProvider *provider = [[ASAuthorizationAppleIDProvider alloc]init];
              [provider getCredentialStateForUserID:userId completion:^(ASAuthorizationAppleIDProviderCredentialState credentialState, NSError * _Nullable error) {
                  BOOL authorized = NO;
                  switch (credentialState) {
                      case ASAuthorizationAppleIDProviderCredentialAuthorized:
                          authorized = YES;
                          break;
                      default:
                          authorized = NO;
                          break;
                  }
                  completeHandler(authorized);
              }];
          } else {
              completeHandler(NO);
          }
      }
      
      
      
      

服务器校验

  • Sign in With Apple之服务端验证 - https://www.jianshu.com/p/8646f599c627
    • golang 实例代码: https://github.com/pyihe/apple_validator

根据上面可以得出验证 IdentityToken 的步骤为:

  1. .为分隔点, 将IdentityToken分隔为三部分, 第三部分为签名, 留着用于验证
  2. 使用Base64URL解码对应的Header和Payload, 并JSON反序列化为对应的结构体(或者键值对), 并且对Payload中相应对值进行验证,如exp, sub, iat, aud
  3. 通过 接口 从Apple Server获取RSA公钥,接口地址 https://appleid.apple.com/auth/keys, 这里需要注意, 获取到的结果通常为两个,需要用选择与Header中的kid值匹配的那个Key
  4. 步骤3返回的Key中包含了RSA公钥中的NE的值,同样是用Base64URL编码后的值, 需要解码, 然后再构造RSA公钥
  5. 得到公钥后,将步骤1中得到的Base64URL编码的Header和Payload再次拼接起来,然后调用rsa.VerifyPKCS1v15()方法进行签名验证, 注意这里的Hash类型为SHA256

踩坑

认证错误

错误: Error Domain=com.apple.AuthenticationServices.AuthorizationError Code=1000

解决办法: 在 singing & capabilities 中加入 sign in with apple 即可.


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蝶泳奈何桥.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值