在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端的驗證流程也有十分大的區別,讓人頭大。
更多內容能夠關注個人博客