在WWDC 2019,苹果推出了Sign In with Apple
这一iOS 13的新特性,用户能够直接利用苹果ID登录应用,免去了输入邮箱、密码,验证登录邮箱等繁琐的步骤。同时Sign In with Apple
提供了跨平台特性和安全性的提升。ios
另外一方面它也提出了新的审核要求,在新的要求中提到但凡包含了第三方登陆的应用,则一样须要适配Sign In with Apple
,不然会有审核风险:git
Sign In with Apple will be available for beta testing this summer. It will be required as an option for users in apps that support third-party sign-in when it is commercially available later this year.github
另外使用Sign In with Apple
须要用户开启了两步认证,若是没有开启则会在第一次使用时提示开启,不开启将没法使用。web
添加Sign in with Apple
的capability
:算法
并在项目中加入AuthenticationServices.framework
便可。当须要使用时,须要在文件中添加<AuthenticationServices/AuthenticationServices.h>
引用。api
登陆按钮可使用苹果推荐的按钮ASAuthorizationAppleIDButton
,具体的设计样式能够参看这里,大体的样式以下:数组
注意:安全
在新用户点击内置的登陆按钮,指望使用苹果ID进行注册和登陆时,咱们须要使用ASAuthorizationAppleIDProvider
来建立一个ASAuthorizationAppleIDRequest
请求,在这个请求中,咱们能够配置一个ASAuthorizationScope
数组,来规定须要用户提供什么样的信息,目前ASAuthorizationScope
仅包含两个:bash
须要注意的是,须要提供电子邮件时,用户是能够选择隐藏真实的邮件地址,这样获取到的邮件地址是这样的:服务器
r45N934br1@privaterelay.appleid.com
该邮箱收到的邮件会转发到用户真实的邮箱上。另外须要提供姓名时,用户是能够对姓名进行修改的,并且是任意修改。
在用户填写完姓名与选择是否隐藏邮箱后,便可以输入苹果ID的密码,并自动完成注册过程,若是无网络会没法继续,停留在在这个页面。
代码实例:
// 建立请求
ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new];
ASAuthorizationAppleIDRequest *request = appleIDProvider.createRequest;
[request setRequestedScopes:@[ASAuthorizationScopeFullName,ASAuthorizationScopeEmail]];
复制代码
若是咱们的APP用户以前已经登录过,而且在keyChain
上保存了用户名和密码时,能够同时使用ASAuthorizationAppleIDProvider
和ASAuthorizationPasswordProvider
来建立请求
这种方式是针对用户已经使用帐号密码登陆过该app,而且已经将他们保存到keyChain
中的状况,因为对我如今项目目前的状态来讲意义不大,因此不在本文进行讨论。
发起请求须要用到ASAuthorizationController
,它能够同时发起多个Provider
的请求。
代码实例:
// 发起请求
ASAuthorizationController *controller = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
controller.delegate = self;
controller.presentationContextProvider = self;
[controller performRequests];
复制代码
在发起请求后就会出现Sign In with Apple
的UI,在用户完成了登陆等操做后会返回请求结果由咱们app进行处理。
ASAuthorizationController
在其中提供了ASAuthorizationControllerDelegate
代理,用于请求结果的回调,代理提供了两个代理方法:
成功回调:
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization
失败回调
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error
在成功的回调中,咱们可以获取一个ASAuthorization
对象,这个对象有provider
与credential
这两个属性,其中provider
属性可以让咱们知道是哪一类的Provider
发起的请求,而credential
则是苹果帐号登陆结果的一个认证。
在“新用户登陆”的状况下,返回的credential
为ASAuthorizationAppleIDCredential
,而“旧用户登陆”的状况下,则返回ASPasswordCredential
。
咱们侧重于ASAuthorizationAppleIDCredential
属性的解析:
- User ID: 苹果用户惟一标识符,它在同一个开发者帐号下的全部 App 下是同样的,咱们能够用它来与后台的帐号体系绑定起来(相似于微信的
OpenID
)。- Verification Data: 包括
identityToken
,authorizationCode
。用于传给开发者后台服务器,而后开发者服务器再向苹果的身份验证服务端验证本次受权登陆请求数据的有效性和真实性。- Account Information: Name, verified email,苹果用户信息,包括全名、邮箱等。
- Real User Indicator: 用于判断当前登陆的苹果帐号是不是一个真实用户,取值有:
unsupported
、unknown
、likelyReal
。
代码实例:
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization {
//Sign with Apple 成功
if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
//此时为使用Sign With Apple 方式登陆
ASAuthorizationAppleIDCredential *credential = authorization.credential;
NSString *userID = credential.user;
NSString *fullname = credential.fullName;
NSData *token = credential.identityToken
//继续进行客户端后台登陆验证
}
}
复制代码
总体流程与普通的第三方登陆十分类似,一样须要获取用户ID与token,交给后台验证本次登陆的有效性。
ASAuthorizationAppleIDProvider
来完成登陆,若是登陆成功,苹果将会返回以下数据:
- User ID: 苹果用户惟一标识符,它在同一个开发者帐号下的全部 App 下是同样的,咱们能够用它来与后台的帐号体系绑定起来(相似于微信的
OpenID
)。- Verification Data: 包括
identityToken
,authorizationCode
。用于传给开发者后台服务器,而后开发者服务器再向苹果的身份验证服务端验证本次受权登陆请求数据的有效性和真实性。- Account Information: 苹果用户信息,包括全名、邮箱等,登陆时用户能够选择隐藏真实的邮件地址和随意修改姓名。
- Real User Indicator: 用于判断当前登陆的苹果帐号是不是一个真实用户,取值有:
unsupported
、unknown
、likelyReal
。
identityToken
, authorizationCode
, userID
这三个参数传给服务器,用于验证本次登陆的有效性。其中identityToken
是一个通过签名的JSON Web Token(JWT),它包含了:
它分为了三个部分:
- header: 包括了key id 与加密算法
- payload:
- iss: 签发机构,苹果
- aud: 接收者,目标app
- exp: 过时时间
- iat: 签发时间
- sub: 用户id
- c_hash: 一个哈希数列,做用未知
- auth_time: 签名时间
- signature: 用于验证JWT的签名
服务端在获取客户端发出的identityToken
后,须要进行以下步骤:
pem
对JWT进行验证identityToken
经过验证,则能够根据其payload中的内容进行验证等操做token验证原理:
由于
idnetityToken
使用非对称加密 RSASSA【RSA签名算法】 和 ECDSA【椭圆曲线数据签名算法】,当验证签名的时候,利用公钥来解密Singature,当解密内容与base64UrlEncode(header) + "." + base64UrlEncode(payload)
的内容彻底同样的时候,表示验证经过。
防止中间人攻击原理:
该token是苹果利用私钥生成的一段JWT,并给出公钥咱们对token进行验证,因为中间人并无苹果的私钥,因此它生成出来的token是没有办法利用苹果给出的公钥进行验证的,确保的token的安全性。
用户利用Apple ID
进行登陆,因此应该对ID退出登陆等状况进行处理。另外用户在利用Apple ID
登陆后,能够在设置页面删除曾经登陆过的应用,相似于解除绑定的操做,这时应用也须要作对应处理。
苹果提供了一个快速的API来让咱们查询用户的Apple ID
状态:
- [ASAuthorizationAppleIDProvider getCredentialStateForUserID:completion:]
复制代码
这个接口利用在登陆时获取的userID
,可以快速返回账号状态:
- authorized: 已认证
- notFound: 用户可能还没有将账号与Apple ID绑定
- revoked: 帐号已经注销
这个方法应该在启动时与应用返回前台时调用,确保帐号状态可以及时更新。
另外苹果在与后台验证一块的文档过于语焉不详,web端与app端的验证流程也有十分大的区别,让人头大。
更多内容能够关注个人博客