• Apple ID 登录


    Apple登录可以看做第三方登录的一种,即使用Apple ID登录,前提是你已经有了苹果开发者账号,直接进入主题吧。

    添加App IDs
    在这里插入图片描述
    填写Bundle ID
    在这里插入图片描述
    勾选“Sign In with Apple”
    在这里插入图片描述
    创建Key
    苹果将使用公钥/私钥对作为OAuth客户端机密,其中客户端机密实际上是一个签名的JWT,下一步需要向Apple注册新的私钥。
    在这里插入图片描述
    创建完成后会生成一个Key ID以及Key文件。下载key文件,其实就是一个.p8文件,双击可打开这个文件里面有需要的key,这个文件很重要,而且只能下载一次,请妥善保存!!!
    在这里插入图片描述
    在这里插入图片描述

    生成客户端密钥(Client Secret)

    苹果要求您自己从私钥中导出客户端密钥,而不是静态客户端密钥。他们使用JWT标准,使用带有P-256曲线和SHA256散列的椭圆曲线算法。换句话说,他们使用ES256JWT算法。有些JWT库不支持椭圆曲线方法,所以在开始尝试之前,请确保您的库支持椭圆曲线。RubyJWT库支持这种算法,因此我们将使用它来生成秘密。

    首先,确保已安装Ruby,然后从命令行运行以下命令来安装JWT gem:

    gem install jwt
    
    • 1

    根据上面准备的 Bundle IDTeam IDKey IDAuthKey_xxx.p8就可以生成client_secret(客户端密钥),我们简单创建一个txt文件,命名为secret_gen,然后将后缀改为.rb,即secret_gen.rb。模版内容如下:

    require 'jwt'
    
    key_file = ''
    team_id = ''
    client_id = ''
    key_id = ''
    
    ecdsa_key = OpenSSL::PKey::EC.new IO.read key_file
    
    headers = {
      'kid' => key_id
    }
    
    claims = {
    	'iss' => team_id,
    	'iat' => Time.now.to_i,
    	'exp' => Time.now.to_i + 86400*180,
    	'aud' => 'https://appleid.apple.com',
    	'sub' => client_id,
    }
    
    token = JWT.encode claims, ecdsa_key, 'ES256', headers
    
    puts token
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    该代码使用ES256算法生成JWT,其中包含少量声明。JWT将在6个月内到期,这是苹果允许的最长使用期限。如果您在每次用户进行身份验证时都生成一个新的客户端密钥JWT,那么您应该使用更短的到期日期,但这允许我们生成一次密钥并在示例应用程序中轻松使用。

    这里将用到我们已经准备好的四个数据,现在您可以从命令行运行它,它将输出一个JWT:
    在这里插入图片描述

    ruby xxx/xx/xx/secret_gen.rb
    
    • 1

    在这里插入图片描述
    这个eyJra.....RuAQ就是我们需要的客户端密钥,接下来我们验证下这个密钥是否有效。

    在项目中配置苹果登录

    首先导入头文件#import ,然后调起苹果授权页面

        if (@available(iOS 13.0, *)) {
    
            ASAuthorizationAppleIDProvider *appleIdProvider = [[ASAuthorizationAppleIDProvider alloc] init];
            ASAuthorizationAppleIDRequest *authAppleIDRequest = [appleIdProvider createRequest];
            /*
             //慎用 ASAuthorizationPasswordRequest
             //当启用ASAuthorizationPasswordRequest且停止使用Apple ID(真机-设置-账户-密码与安全性-使用您Apple ID的App-App列表-停止使用 Apple ID, 如果KeyChain里面没有登录信息且重新使用苹果授权登录(Sign in with Apple)会报未知错误
             ASAuthorizationPasswordRequest* authPasswordRequest = [[[ASAuthorizationPasswordProvider alloc] init] createRequest];
             
             NSMutableArray <ASAuthorizationRequest*> * array = [NSMutableArray arrayWithCapacity:2];
             if(authAppleIDRequest) [array addObject:authAppleIDRequest];
             if(authPasswordRequest) [array addObject:authPasswordRequest];
             
             NSArray <ASAuthorizationRequest*> * requests = [array copy];
             ASAuthorizationController* authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:requests];
             */
            authAppleIDRequest.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
            
            //由ASAuthorizationAppleIDProvider创建的授权请求 管理授权请求的控制器
            ASAuthorizationController* authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[authAppleIDRequest]];
            //设置授权控制器通知授权请求的成功与失败的代理
            authorizationController.delegate = self;
            //设置提供 展示上下文的代理,在这个上下文中 系统可以展示授权界面给用户
            authorizationController.presentationContextProvider = self;
            //在控制器初始化期间启动授权流
            [authorizationController performRequests];
            
        } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    代理回调ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding

    //授权成功
    - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0))
    {
        NSString *userID = nil;
        NSString *userName = nil;
        NSString *userEmail = nil;
        
        EVAppleLoginCredentialModel *model = [[EVAppleLoginCredentialModel alloc] init];
        
        if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
            
            ASAuthorizationAppleIDCredential *credential = authorization.credential;
            
            //苹果用户唯一标识符,该值在同一个开发者账号下的所有App下是一样的,开发者可以用该唯一标识符与自己后台系统的账号体系绑定起来。
            userID = credential.user;
        
            //苹果用户信息 如果授权过,无法再次获取该信息
            userEmail = credential.email;
            NSPersonNameComponents *fullName = credential.fullName;
            userName = [NSString stringWithFormat:@"%@%@", fullName.familyName, fullName.givenName];
            
            //用于判断当前登录的苹果账号是否是一个真实用户,取值有:unsupported、unknown、likelyReal
            ASUserDetectionStatus realUserStatus = credential.realUserStatus;
    
            //服务器验证需要使用的参数
            NSString *authorizationCode = [[NSString alloc] initWithData:credential.authorizationCode encoding:NSUTF8StringEncoding];
            NSString *identityToken = [[NSString alloc] initWithData:credential.identityToken encoding:NSUTF8StringEncoding];
            
            NSLog(@"authorization [ASAuthorizationAppleIDCredential] successfully");
            NSLog(@"authorization userID: %@", userID);
            NSLog(@"authorization userName: %@", userName);
            NSLog(@"authorization userEmail: %@", userEmail);
            NSLog(@"authorization realUserStatus: %@", @(realUserStatus));
            NSLog(@"authorization authorizationCode: %@", authorizationCode);
            NSLog(@"authorization identityToken: %@", identityToken);
            
        } else if ([authorization.credential isKindOfClass:[ASPasswordCredential class]]) {
            //用户登录使用现有的密码凭证
            ASPasswordCredential *passwordCredential = authorization.credential;
    
            //密码凭证对象的用户标识 用户的唯一标识和密码
            userID = passwordCredential.user;
            NSString *password = passwordCredential.password;
        
            NSLog(@"authorization [ASPasswordCredential] successfully");
            NSLog(@"authorization userID: %@", userID);
            NSLog(@"authorization password: %@", password);
            
            model.user = passwordCredential.user;
            
            userName = @"";
            userEmail = @"";
            
        } else {
            assert(0);
        }
        
        //数据校验通过后,将这些数据发送给服务器进行验证,等待服务器的回应
        //如果用户选择隐藏邮箱的,email获取不到,其他数据正常,服务端匹配标准userID
        if (![ZJUtils isEmpty:userID]) {
    
            //在授权成功的回调中拿到服务器所需要的参数传给后台
            //至此我们所需要做的已经完成了,看后台的验证就行了
            if (self.appleLoginBlock) {
                self.appleLoginBlock(0, @"苹果授权成功", model);
            }
            
        } else {
            //授权数据异常
            if (self.appleLoginBlock) {
                self.appleLoginBlock(-1, @"苹果授权失败", nil);
            }
        }
    }
    
    //授权失败
    - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0))
    {
        NSString *errorMsg = nil;
        UIWindow *window = [UIApplication sharedApplication].keyWindow;
        switch (error.code) {
            case ASAuthorizationErrorCanceled:
                errorMsg = @"用户取消了授权请求";
                break;
            case ASAuthorizationErrorFailed:
                errorMsg = @"授权请求失败";
                break;
            case ASAuthorizationErrorInvalidResponse:
                errorMsg = @"授权请求响应无效";
                break;
            case ASAuthorizationErrorNotHandled:
                errorMsg = @"未能处理授权请求";
                break;
            case ASAuthorizationErrorUnknown:
                errorMsg = @"授权请求失败未知原因";
                break;
        }
        NSLog(@"苹果授权状态:%@", errorMsg);
    }
    
    #pragma mark ASAuthorizationControllerPresentationContextProviding
    - (ASPresentationAnchor) presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0))
    {
        return self.view.window;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

    - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization 回调中我们拿到 authorizationCode 作为验证的参数。

    打开postman我们测试下:

    https://appleid.apple.com/auth/token?client_id=xxx&client_secret=xxx&code=xxx&grant_type=authorization_code&redirect_uri=https://appleid.apple.com
    
    • 1

    在这里插入图片描述

    到这里就基本完成了,其实苹果登录大部分工作都是后台在做,包括client_secret生成等,我们客户端只需要在拿到苹果授权的数据给后端就可以了,后端去做校验。

    参考:What the Heck is Sign In with Apple?

  • 相关阅读:
    为视觉语言多模态模型进行偏好优化
    opencv 通过滑动条调整阈值处理、边缘检测、轮廓检测、模糊、色调调整和对比度增强参数 并实时预览效果
    2020美亚个人赛复盘
    【java计算机毕设】基于J2EE的仓库管理系统设计与开发源码带文档MySQL ssm vue maven前后端可分离也可不分离
    聊一聊 tcp/ip 在.NET故障分析的重要性
    计算机毕业设计论文_s2sh+mysql问卷调查系统|投票项目源码
    面试官说入职以后要参与重构Kafka内核,我吓的不敢接offer!
    ArmSoM Rockchip系列产品 通用教程 之 HDMI-IN使用
    [面试]我们常说的负载均衡是什么东西?
    WPF Image设置为圆形,并有折叠
  • 原文地址:https://blog.csdn.net/SSY_1992/article/details/127976140