iOS实现苹果第三方登录功能2020-03-18

Sign In with Apple 让用户可以使用面容 ID 或触控 ID 来轻松进行身份认证,而内置的双重认证则再增添一层安全保障

1.实现

实现分四大部分:

1、创建Sign in    with Apple Button.

2、跟用户提出授权请求.

3、根据用户的授权来验证用户.

4、处理用户授权变更.


2.开启 Sign in with Apple 功能

登录开发者网站,在需要添加 Sign in with Apple 功能的 Identifier 开启功能。

1.登陆developer账号,在app bundle ID的Capabilities里,打勾Sign In with Apple



2.在Xcode(Xcode 11.0 Beta或更新版本)的工程里面 Signing & Capabilities 开启Sign in with Apple 功能。

3.代码集成

3.1创建登录按钮

官方提供了一个 ASAuthorizationAppleIDButton (继承自UIControl),使用这个来创建一个登录按钮。

Apple 提供的登录按钮有三种外观:白色,带有黑色轮廓线的白色和黑色。∙ 文案有两种:Sign In

with Apple 和 Continue with Apple。(具体使用哪个文案,根据自身业务需求来定)另外,按钮宽高默认值为 {width:130, height:30}。

对于 ASAuthorizationAppleIDButton 我们能够自定义的东西比较少,比如背景色不能更改,文案只有两种可选,并且值不能修改,可以调整的只有圆角cornerRadius和size 。

3.2Authorization 发起授权登录请求

- (void)signInWithApple API_AVAILABLE(ios(13.0))

{

   ASAuthorizationAppleIDProvider *provider =[[ASAuthorizationAppleIDProvider alloc] init];

   ASAuthorizationAppleIDRequest *request = [provider createRequest];

   request.requestedScopes = @[ASAuthorizationScopeFullName,ASAuthorizationScopeEmail];


   ASAuthorizationController *vc = [[ASAuthorizationController alloc]initWithAuthorizationRequests:@[request]];

    vc.delegate =self;// 会提供请求后的结果回调,比如用户请求失败,或者用户请求成功。

   vc.presentationContextProvider = self;// 是为验证的用户界面提供所需的window

    [vcperformRequests];

}

3.3授权回调处理来验证用户

下面是 ASAuthorizationControllerDelegate 方法,一个是授权成功的回调,一个是失败的回调。

#pragma mark - ASAuthorizationControllerDelegate


- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0))

{

    if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]])       {

        ASAuthorizationAppleIDCredential*credential = authorization.credential;


        NSString*state = credential.state;

        NSString*userID = credential.user;

        NSPersonNameComponents*fullName = credential.fullName;

        NSString*email = credential.email;

        NSString*authorizationCode = [[NSString alloc] initWithData:credential.authorizationCode encoding:NSUTF8StringEncoding]; // refresh token

        NSString*identityToken = [[NSString alloc] initWithData:credential.identityToken encoding:NSUTF8StringEncoding]; // access token

        ASUserDetectionStatus realUserStatus= credential.realUserStatus;


        NSLog(@"state: %@", state);

        NSLog(@"userID: %@", userID);

        NSLog(@"fullName: %@", fullName);

        NSLog(@"email: %@", email);

        NSLog(@"authorizationCode: %@", authorizationCode);

        NSLog(@"identityToken: %@", identityToken);

        NSLog(@"realUserStatus: %@", @(realUserStatus));

    }

}

//当我们授权成功后,我们可以在 authorizationController:didCompleteWithAuthorization: 这个代理方法中获取到ASAuthorizationAppleIDCredential ,通过这个可以拿到用户的 userID、email、fullName、authorizationCode、identityToken 以及 realUserStatus 等信息。


- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0))

{

    NSString*errorMsg = nil;

    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);

}

这些信息具体含义和用途:

∙ User ID: Unique, stable, team-scoped user ID,苹果用户唯一标识符,该值在同一个开发者账号下的所有 App 下是一样的,开发者可以用该唯一标识符与自己后台系统的账号体系绑定起来。

∙ Verification data: Identity token, code,验证数据,用于传给开发者后台服务器,然后开发者服务器再向苹果的身份验证服务端验证本次授权登录请求数据的有效性和真实性,详见 Sign In with Apple REST API。如果验证成功,可以根据userIdentifier 判断账号是否已存在,若存在,则返回自己账号系统的登录态,若不存在,则创建一个新的账号,并返回对应的登录态给 App。

∙ Account information: Name, verified email,苹果用户信息,包括全名、邮箱等。

∙ Real user indicator: High confidence indicator that

likely real user,用于判断当前登录的苹果账号是否是一个真实用户,取值有:unsupported、unknown、likelyReal。

失败情况会走 authorizationController:didCompleteWithError: 这个方法,具体看代码吧。

[if !supportLists]3.4.  [endif]处理用户授权变更

通过上面的步骤一个完整的授权,已经完成。BUT,我们还需要处理一些 Case。

∙ 用户终止 App 中使用 Sign in with Apple 功能

∙ 用户在设置里注销了AppleId

这些情况下,App 需要获取到这些状态,然后做退出登录操作,或者重新登录。我们需要在 App 启动的时候,通过 getCredentialState:completion: 来获取当前用户的授权状态。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {


    if (@available(iOS 13.0, *)) {

        NSString*userIdentifier = 钥匙串中取出的 userIdentifier;

        if (userIdentifier) {

            ASAuthorizationAppleIDProvider*appleIDProvider = [ASAuthorizationAppleIDProvider new];

            [appleIDProvider getCredentialStateForUserID:userIdentifier

                                              completion:^(ASAuthorizationAppleIDProviderCredentialState credentialState,

                                                           NSError* _Nullable error)

            {

                switch (credentialState) {

                    case ASAuthorizationAppleIDProviderCredentialAuthorized:

                        // The Apple ID credential is valid

                        break;

                    case ASAuthorizationAppleIDProviderCredentialRevoked:

                        // Apple ID Credential revoked, handle unlink

                        break;

                    case ASAuthorizationAppleIDProviderCredentialNotFound:

                        // Credential not found, show login UI

                        break;

                }

            }];

        }

    }


    return YES;

}

ASAuthorizationAppleIDProviderCredentialState 解析如下:∙ASAuthorizationAppleIDProviderCredentialAuthorized授权状态有效;∙ASAuthorizationAppleIDProviderCredentialRevoked上次使用苹果账号登录的凭据已被移除,需解除绑定并重新引导用户使用苹果登录;∙ASAuthorizationAppleIDProviderCredentialNotFound未登录授权,直接弹出登录页面,引导用户登录。

另外,在 App 使用过程中,你还可以通过通知方法来监听 revoked 状态,可以添加 ASAuthorizationAppleIDProviderCredentialRevokedNotification 这个通知,收到这个通知的时候,我们可以:

∙Sign user out on this device

∙Guide to sign in again

具体怎么添加和处理,可以根据业务需求来决定。

- (void)observeAppleSignInState

{

    if (@available(iOS 13.0, *)) {

        [[NSNotificationCenter defaultCenter] addObserver:self

                                                 selector:@selector(handleSignInWithAppleStateChanged:)

                                                     name:ASAuthorizationAppleIDProviderCredentialRevokedNotification

                                                   object:nil];

    }

}


- (void)handleSignInWithAppleStateChanged:(NSNotification *)notification

{

    // Sign the user out, optionally guide them to sign in again

    NSLog(@"%@", notification.userInfo);

}

[if !supportLists]3.5.  [endif]One more thing

除此之外,苹果还把 iCloud KeyChain password 集成到了这套 API 里,我们在使用的时候,只需要在创建 request 的时候,多创建一个 ASAuthorizationPasswordRequest,这样如果KeyChain 里面也有登录信息的话,可以直接使用里面保存的用户名和密码进行登录。代码如下:

- (void)signInWithAppleAPI_AVAILABLE(ios(13.0))

{

    ASAuthorizationAppleIDProvider*appleIDProvider = [ASAuthorizationAppleIDProvider new];

    ASAuthorizationAppleIDRequest*authAppleIDRequest = [appleIDProvider createRequest];

    ASAuthorizationPasswordRequest*passwordRequest = [[ASAuthorizationPasswordProvider new] createRequest];


    NSMutableArray<ASAuthorizationRequest *>* array = [NSMutableArray arrayWithCapacity:2];

    if (authAppleIDRequest) {

        [array addObject:authAppleIDRequest];

    }

    if (passwordRequest) {

        [array addObject:passwordRequest];

    }

    NSArray<ASAuthorizationRequest *>* requests = [array copy];


    ASAuthorizationController*authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:requests];

    authorizationController.delegate = self;

    authorizationController.presentationContextProvider = self;

    [authorizationController performRequests];

}


#pragma mark - ASAuthorizationControllerDelegate


- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0))

{

    if ([authorization.credential isKindOfClass:[ASPasswordCredential class]]) {

        ASPasswordCredential*passwordCredential = authorization.credential;

        NSString*userIdentifier = passwordCredential.user;

        NSString*password = passwordCredential.password;


        NSLog(@"userIdentifier: %@", userIdentifier);

        NSLog(@"password: %@", password);

    }

}


4.本地化:必要且重要的一点

按钮显示的文字都是英文的,而且我们不可以修改文字,总不能在国内也展示个英文只需要添加本地化支持就可以了,此方法适用于一切系统文案,比如我们创建的 UIBarButtonSystemItemSave ,添加简体中文支持后,他显示的文案就变成 保存



5.Sign In with Apple 和 Continue with Apple登录的区别

只是文案的不同而已,中文意思分别为:通过Apple登录、通过Apple继续

6.苹果第三方登录与己方后台服务账号相互认证问题

在第三方登录出现之前,大部分用户账户注册与登录流程都是双输的局面:

用户方面:为了安全性而设置的密码往往复杂又冗余,既容易忘记输入又很麻烦,同时各种难度的验证码和各种验证问题,不仅过滤机器人也能过滤真人,加上手机验证码等等繁琐步骤,让注册登录变得异常烦心。

服务商方面:加上各种验证步骤会明显降低用户体验,可是不加又会导致用户账户不安全和各种机器人账号泛滥,更不用说还要自己探索实现各种验证步骤,一不小心就会出现漏洞。很多时候最后这些繁琐的步骤也没有挡住日益智能的机器人,反而挡住了很多正常用户。

[if !supportLists]·      [endif]为了解决上面提到的各种问题,开放授权(OAuth)在 2010 年应运而生。简单来讲它是一个开放标准,允许用户在不提供密码的情况下,授权接入了 A 网站第三方登录支持的第三方应用,访问自己在 A 网站上的特定数据

[if !supportLists]·      [endif]接着为了解决安全性问题,OAuth

2.0 诞生了,同时有人发现了这个标准很适合用来登录验证用户身份,于是基于此延伸的标准,专门解决第三方客户端标使用身份认证的问题的 OIDC(OpenID Connect) 诞生了。现在我们见到的大多数第三方登录都是基于 OIDC 或者利用 OAuth 2.0 修改实现。

第三方登录的原理

因为 QQ/微信/微博等平台(以下简称第一方平台)拥有完整的登录验证步骤,用户在上面已经充分地证明了「我是真的我」。所以,第三方网站与服务只要接入了这些平台的第三方登录 SDK(开发组件),用户点击第三方登录按钮时:

网站或服务会通过第三方登录 SDK 向第一方平台发送登录请求。

第一方平台接收到登录请求,确认用户设备上有没有已经登录的第一方平台账户,如果没有用户需要先手动登录第一方平台。

如果用户已经在设备上登录了第一方平台账户,那么第一方平台会返回唯一的不变的 ID(OpenID)

第三方网站与服务将这个 ID 储存到自己的服务器上,以后就能通过这个 ID 确认用户了。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342