Sign In with Apple 讓用戶可以使用面容 ID 或觸控 ID 來輕松進(jìn)行身份認(rèn)證,而內(nèi)置的雙重認(rèn)證則再增添一層安全保障
1.實(shí)現(xiàn)
實(shí)現(xiàn)分四大部分:
1郑气、創(chuàng)建Sign in? ? with Apple Button.
2腰池、跟用戶提出授權(quán)請求.
3示弓、根據(jù)用戶的授權(quán)來驗(yàn)證用戶.
4、處理用戶授權(quán)變更.
2.開啟 Sign in with Apple 功能
登錄開發(fā)者網(wǎng)站跨跨,在需要添加 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創(chuàng)建登錄按鈕
官方提供了一個 ASAuthorizationAppleIDButton (繼承自UIControl)椭盏,使用這個來創(chuàng)建一個登錄按鈕吻商。
Apple 提供的登錄按鈕有三種外觀:白色艾帐,帶有黑色輪廓線的白色和黑色。? 文案有兩種:Sign In
with Apple 和 Continue with Apple准浴。(具體使用哪個文案捎稚,根據(jù)自身業(yè)務(wù)需求來定)另外今野,按鈕寬高默認(rèn)值為 {width:130, height:30}。
對于 ASAuthorizationAppleIDButton 我們能夠自定義的東西比較少匾南,比如背景色不能更改蛔外,文案只有兩種可選夹厌,并且值不能修改,可以調(diào)整的只有圓角cornerRadius和size 臂聋。
3.2Authorization 發(fā)起授權(quán)登錄請求
- (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;// 會提供請求后的結(jié)果回調(diào)孩等,比如用戶請求失敗,或者用戶請求成功冰垄。
???vc.presentationContextProvider = self;// 是為驗(yàn)證的用戶界面提供所需的window
??? [vcperformRequests];
}
3.3授權(quán)回調(diào)處理來驗(yàn)證用戶
下面是 ASAuthorizationControllerDelegate 方法虹茶,一個是授權(quán)成功的回調(diào)隅要,一個是失敗的回調(diào)步清。
#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));
??? }
}
//當(dāng)我們授權(quán)成功后廓啊,我們可以在 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= @"用戶取消了授權(quán)請求";
??????????? break;
??????? case ASAuthorizationErrorFailed:
??????????? errorMsg= @"授權(quán)請求失敗";
??????????? break;
??????? case ASAuthorizationErrorInvalidResponse:
??????????? errorMsg= @"授權(quán)請求響應(yīng)無效";
??????????? break;
??????? case ASAuthorizationErrorNotHandled:
??????????? errorMsg= @"未能處理授權(quán)請求";
??????????? break;
??????? case ASAuthorizationErrorUnknown:
???????? ???errorMsg= @"授權(quán)請求失敗未知原因";
??????????? break;
??? }
??? NSLog(@"%@", errorMsg);
}
這些信息具體含義和用途:
? User ID: Unique, stable, team-scoped user ID居暖,蘋果用戶唯一標(biāo)識符太闺,該值在同一個開發(fā)者賬號下的所有 App 下是一樣的,開發(fā)者可以用該唯一標(biāo)識符與自己后臺系統(tǒng)的賬號體系綁定起來蟀淮。
? Verification data: Identity token, code,驗(yàn)證數(shù)據(jù)涨缚,用于傳給開發(fā)者后臺服務(wù)器仗岖,然后開發(fā)者服務(wù)器再向蘋果的身份驗(yàn)證服務(wù)端驗(yàn)證本次授權(quán)登錄請求數(shù)據(jù)的有效性和真實(shí)性览妖,詳見 Sign In with Apple REST API讽膏。如果驗(yàn)證成功,可以根據(jù)userIdentifier 判斷賬號是否已存在俐末,若存在奄侠,則返回自己賬號系統(tǒng)的登錄態(tài)垄潮,若不存在,則創(chuàng)建一個新的賬號旅急,并返回對應(yīng)的登錄態(tài)給 App牡整。
? Account information: Name, verified email逃贝,蘋果用戶信息,包括全名潦闲、郵箱等歉闰。
? Real user indicator: High confidence indicator that
likely real user,用于判斷當(dāng)前登錄的蘋果賬號是否是一個真實(shí)用戶凹炸,取值有:unsupported昼弟、unknown舱痘、likelyReal。
失敗情況會走 authorizationController:didCompleteWithError: 這個方法塌碌,具體看代碼吧旬盯。
[if !supportLists]3.4.? [endif]處理用戶授權(quán)變更
通過上面的步驟一個完整的授權(quán),已經(jīng)完成接剩。BUT懊缺,我們還需要處理一些 Case培他。
? 用戶終止 App 中使用 Sign in with Apple 功能
? 用戶在設(shè)置里注銷了AppleId
這些情況下靶壮,App 需要獲取到這些狀態(tài)员萍,然后做退出登錄操作碎绎,或者重新登錄。我們需要在 App 啟動的時候奸晴,通過 getCredentialState:completion: 來獲取當(dāng)前用戶的授權(quán)狀態(tài)寄啼。
- (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授權(quán)狀態(tài)有效墩划;?ASAuthorizationAppleIDProviderCredentialRevoked上次使用蘋果賬號登錄的憑據(jù)已被移除乙帮,需解除綁定并重新引導(dǎo)用戶使用蘋果登錄;?ASAuthorizationAppleIDProviderCredentialNotFound未登錄授權(quán)驾茴,直接彈出登錄頁面锈至,引導(dǎo)用戶登錄异吻。
另外诀浪,在 App 使用過程中,你還可以通過通知方法來監(jiān)聽 revoked 狀態(tài)睛竣,可以添加 ASAuthorizationAppleIDProviderCredentialRevokedNotification 這個通知射沟,收到這個通知的時候与境,我們可以:
?Sign user out on this device
?Guide to sign in again
具體怎么添加和處理摔刁,可以根據(jù)業(yè)務(wù)需求來決定。
- (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 里拗引,我們在使用的時候矾削,只需要在創(chuàng)建 request 的時候,多創(chuàng)建一個 ASAuthorizationPasswordRequest欲间,這樣如果KeyChain 里面也有登錄信息的話括改,可以直接使用里面保存的用戶名和密碼進(jìn)行登錄嘱能。代碼如下:
- (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.本地化:必要且重要的一點(diǎn)
按鈕顯示的文字都是英文的惹骂,而且我們不可以修改文字对粪,總不能在國內(nèi)也展示個英文只需要添加本地化支持就可以了,此方法適用于一切系統(tǒng)文案纱扭,比如我們創(chuàng)建的 UIBarButtonSystemItemSave 乳蛾,添加簡體中文支持后鄙币,他顯示的文案就變成 保存
5.Sign In with Apple 和 Continue with Apple登錄的區(qū)別
只是文案的不同而已十嘿,中文意思分別為:通過Apple登錄、通過Apple繼續(xù)
6.蘋果第三方登錄與己方后臺服務(wù)賬號相互認(rèn)證問題
在第三方登錄出現(xiàn)之前蹦魔,大部分用戶賬戶注冊與登錄流程都是雙輸?shù)木置妫?/p>
用戶方面:為了安全性而設(shè)置的密碼往往復(fù)雜又冗余版姑,既容易忘記輸入又很麻煩迟郎,同時各種難度的驗(yàn)證碼和各種驗(yàn)證問題,不僅過濾機(jī)器人也能過濾真人聪蘸,加上手機(jī)驗(yàn)證碼等等繁瑣步驟宪肖,讓注冊登錄變得異常煩心表制。
服務(wù)商方面:加上各種驗(yàn)證步驟會明顯降低用戶體驗(yàn),可是不加又會導(dǎo)致用戶賬戶不安全和各種機(jī)器人賬號泛濫控乾,更不用說還要自己探索實(shí)現(xiàn)各種驗(yàn)證步驟么介,一不小心就會出現(xiàn)漏洞。很多時候最后這些繁瑣的步驟也沒有擋住日益智能的機(jī)器人蜕衡,反而擋住了很多正常用戶壤短。
[if !supportLists]·??????[endif]為了解決上面提到的各種問題,開放授權(quán)(OAuth)在 2010 年應(yīng)運(yùn)而生慨仿。簡單來講它是一個開放標(biāo)準(zhǔn),允許用戶在不提供密碼的情況下镰吆,授權(quán)接入了 A 網(wǎng)站第三方登錄支持的第三方應(yīng)用帘撰,訪問自己在 A 網(wǎng)站上的特定數(shù)據(jù)。
[if !supportLists]·??????[endif]接著為了解決安全性問題万皿,OAuth
2.0 誕生了摧找,同時有人發(fā)現(xiàn)了這個標(biāo)準(zhǔn)很適合用來登錄驗(yàn)證用戶身份,于是基于此延伸的標(biāo)準(zhǔn)牢硅,專門解決第三方客戶端標(biāo)使用身份認(rèn)證的問題的 OIDC(OpenID Connect) 誕生了〉旁牛現(xiàn)在我們見到的大多數(shù)第三方登錄都是基于 OIDC 或者利用 OAuth 2.0 修改實(shí)現(xiàn)。
第三方登錄的原理
因?yàn)?QQ/微信/微博等平臺(以下簡稱第一方平臺)擁有完整的登錄驗(yàn)證步驟减余,用戶在上面已經(jīng)充分地證明了「我是真的我」婆赠。所以,第三方網(wǎng)站與服務(wù)只要接入了這些平臺的第三方登錄 SDK(開發(fā)組件)佳励,用戶點(diǎn)擊第三方登錄按鈕時:
網(wǎng)站或服務(wù)會通過第三方登錄 SDK 向第一方平臺發(fā)送登錄請求休里。
第一方平臺接收到登錄請求,確認(rèn)用戶設(shè)備上有沒有已經(jīng)登錄的第一方平臺賬戶赃承,如果沒有用戶需要先手動登錄第一方平臺妙黍。
如果用戶已經(jīng)在設(shè)備上登錄了第一方平臺賬戶,那么第一方平臺會返回唯一的不變的 ID(OpenID)瞧剖。
第三方網(wǎng)站與服務(wù)將這個 ID 儲存到自己的服務(wù)器上拭嫁,以后就能通過這個 ID 確認(rèn)用戶了。