关于Sign in with Apple 后台验证的一些记录
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(); }