2019年10月9號 IOS端新增Sign in with Apple
IOS真是世界上最垃圾的語言,沒有之一,蘋果是世界上最垃圾的公司,沒有之一
關於Sign in with Apple 蘋果官方給的文檔幾乎不提后台如何驗證,只是畫了幾張圖,客戶端把一些參數傳遞給后台,后台使用這些參數去請求 IOS驗證服務器 驗證成功 OK 聽起來很簡單吧
mmp 1.前台給的參數如何解析?
mmp 2.參數怎么傳給IOS服務器?
mmp 3.返回什么就算解析成功?
一概不提,最起碼給個demo啊
翻遍了網上的資源,最終找到一個還算過的去的 https://blog.csdn.net/wpf199402076118/article/details/99677412
博客中大致說了兩種驗證方式:
1.JWT 驗證 博主也貼出了代碼,但是你會發現,代碼跑不通,不知道是代碼沒貼全還是什么鬼,兩個方法你不知到怎么調用
2.授權碼驗證 需要的很多字段你不知道該從哪里獲取,前台無法傳遞給你
經過無數次的試驗和無數次的抓狂,最終有了下面的垃圾代碼,我總覺得哪里還是有問題,代碼貼出來問題接下來說
public static String verify(String jwt, String audience, String subject) throws Exception { String strkey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlxrwmuYSAsTfn+lUu4goZSXBD9ackM9OJuwUVQHmbZo6GW4Fu/auUdN5zI7Y1dEDfgt7m7QXWbHuMD01HLnD4eRtY+RNwCWdjNfEaY/esUPY3OVMrNDI15Ns13xspWS3q+13kdGv9jHI28P87RvMpjz/JCpQ5IM44oSyRnYtVJO+320SB8E2Bw92pmrenbp67KRUzTEVfGU4+obP5RZ09OxvCr1io4KJvEOjDJuuoClF66AT72WymtoMdwzUmhINjR0XSqK6H0MdWsjw7ysyd/JhmqX5CAaT9Pgi0J8lU/pcl215oANqjy7Ob+VMhug9eGyxAWVfu/1u6QJKePlE+wIDAQAB"; PublicKey publicKey = getPublicKey(strkey); JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey); jwtParser.requireIssuer("https://appleid.apple.com"); jwtParser.requireAudience(audience); jwtParser.requireSubject(subject); try { Jws<Claims> claim = jwtParser.parseClaimsJws(jwt); if (claim != null && claim.getBody().containsKey("auth_time")) { return "SUCCESS"; } return "FIALD"; } catch (ExpiredJwtException e) { log.error("apple identityToken expired", e); return "FIALD"; } catch (Exception e) { log.error("apple identityToken illegal", e); return "FIALD"; } } /** * String轉公鑰PublicKey * @param key * @return * @throws Exception */ public static PublicKey getPublicKey(String key) throws Exception { byte[] keyBytes; keyBytes = (new BASE64Decoder()).decodeBuffer(key); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(keySpec); return publicKey; }
說下如何驗證
1.IOS客戶端會調用蘋果的授權登錄一系列操作成功后會從蘋果那獲取一些很基本的信息 比如唯一的userId,一個jwt格式的驗證字符串 identityToken,郵箱 等
2.客戶端授權驗證成功后調用后台接口進行驗證
3.后台解析 jwt的這個字符串,jwt格式的字符串用 “ . ”’ 把整個信息分割成三部分,第一部分是 header 包含了 編碼格式 和秘鑰 id ,第二部分就是一些驗證信息 授權人是誰啊 有效時間啊 第三部分是一二部分加起來后再加密的一個東西,這塊我也沒搞懂,第三部分的解析我到現在也不懂
4. jwt 的 header 和 claim(第二部分) 都是使用Base64編碼過的 使用是需要解碼
第一部分解析出來的
{ "kid": "AIDOPK1", "alg": "RS256" }
第二部分解析出來的
{ "iss": "https://appleid.apple.com", "aud": "**********", "exp": 1570617356, "iat": 1570616756, "sub": "00*****1790047f40335c6c1a.0641", "c_hash": "eqOdpr_**TyHiRymHbPQ", "auth_time": 1570616756 }
5.第二部分的解碼可以獲取到兩個有效信息 aud 和 sub 這兩個在驗證的時候需要使用
6.驗證方法中的key 這個key本來是從蘋果提供的一個get接口中獲取的但是獲取到的格式是下面的東西
{ "keys": [ { "kty": "RSA", "kid": "AIDOPK1", "use": "sig", "alg": "RS256", "n": "lxrwmuYSAsTfn-lUu4goZSXBD9ackM9OJuwUVQHmbZo6GW4Fu_auUdN5zI7Y1dEDfgt7m7QXWbHuMD01HLnD4eRtY-RNwCWdjNfEaY_esUPY3OVMrNDI15Ns13xspWS3q-13kdGv9jHI28P87RvMpjz_JCpQ5IM44oSyRnYtVJO-320SB8E2Bw92pmrenbp67KRUzTEVfGU4-obP5RZ09OxvCr1io4KJvEOjDJuuoClF66AT72WymtoMdwzUmhINjR0XSqK6H0MdWsjw7ysyd_JhmqX5CAaT9Pgi0J8lU_pcl215oANqjy7Ob-VMhug9eGyxAWVfu_1u6QJKePlE-w", "e": "AQAB" } ] }
這個東西怎么用?我沒要找到具體的描述,但是網上網站可以將這個東西轉換正 PublicKey 至於 java代碼中如何轉換我也沒找到,還望找的同學告知下 網上轉換后得到的是是一個字符串 通過一下的方法可以轉換成 PublicKey
public static PublicKey getPublicKey(String key) throws Exception { byte[] keyBytes; keyBytes = (new BASE64Decoder()).decodeBuffer(key); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(keySpec); return publicKey; }
7.驗證 驗證字符串的有效時間是5分鍾
補充:
String jwt = thirdLoginReq.getIdentityToken(); String decode = Base64.decoded(jwt.split("\\.")[1]); String substring = decode.substring(0, decode.indexOf("}")+1); JSONObject jsonObject = JSON.parseObject(substring); String sub = jsonObject.getString("sub"); String aud = jsonObject.getString("aud");
使用到的jar
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.7.0</version> </dependency>
更新 20191010 19:00
經過查閱資料發現可以將 蘋果返回的公鑰轉化成 publicKey 需要引入jar包
<dependency>
<groupId>com.auth0</groupId>
<artifactId>jwks-rsa</artifactId>
<version>0.9.0</version>
</dependency>
String urlData = getUrlData("https://appleid.apple.com/auth/keys"); Map maps = (Map)JSON.parse(urlData); List keys = (List<Map>)maps.get("keys"); Map o = (Map) keys.get(0); Jwk jwa = Jwk.fromValues(o); try { PublicKey publicKey = jwa.getPublicKey(); } catch (InvalidPublicKeyException e) { e.printStackTrace(); }
