先吐槽一下!苹果的开发文档简直就是一坨S

好了!!开发步骤如下:
1.导入JWT Maven 坐标

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

2.闭上眼睛,以GET方式不带参数访问 https://appleid.apple.com/auth/keys。获取*新JSON,对我们有用的就是”n”和”e”,组成JWT公钥(此处官方有变更,现在获取到的keys数组为多个,需要根据下面第三步IOS端获取的identityToken中的kid来找对应下标的keys数组)。

获取到的json内容:
{
“keys”: [
{
“kty”: “RSA”,
“kid”: “AIDOPK1”,
“use”: “sig”,
“alg”: “RS256”,
“n”: “(可能每次都不一样)xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”,
“e”: “AQAB”
}
]
}

ps.我这里使用的是SpringBoot RestTemplate的GET请求方式,然后根据上面的”n”和”e”组成公钥:

AUTH_DOMAIN = https://appleid.apple.com/auth/keys
AppleKeys:请求返回数据的对象
RSA = RSA
public PublicKey getPublicKey() {
try {
String forObject = restTemplate.getForObject(AUTH_DOMAIN, String.class);
if(StringUtils.isEmpty(forObject)){ return null; }
AppleKeys appleKeys = JsonUtils.parseObject(forObject, AppleKeys.class);
List<AppleKeys.Keys> keys = appleKeys.getKeys();
String n = keys.get(0).getN();
String e = keys.get(0).getE();
final BigInteger modulus = new BigInteger(1, Base64.decodeBase64(n));
final BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(e));
final RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
final KeyFactory kf = KeyFactory.getInstance(RSA);
return kf.generatePublic(spec);
} catch (final Exception e) {
log.debug(“[APPLE]ERROR:{}”,e)
}
return null;
}

3.IOS客户端经过一系列操作获取到:identityToken,userID等。将identityToken传递到后台(其他参数没啥用)

4.后台取到IOS客户端传过来的 identityToken 是jwt格式(固定三段式以“.”切割…我们只需要取第二段即可)。然后使用Base64解析出来,里面对我们有用的数据只有三个字段:

“iss”=https://appleid.apple.com(固定签名)(**此字段已被官方替换成 “kid” 用于找到第二步中kid匹配的keys**);
“aud”=APPID
“sub”=**用户的唯一标识**(所以前端不需要额外传递userID)

代码:

String [] identityTokens = identityToken.split(“\\.”);
Map<String, Object> data = JsonUtils.parseMap(new String(Base64.decodeBase64(identityTokens[1]),”UTF-8″),String.class, Object.class);
String iss = (String) data.get(“kid”);
String aud = (String) data.get(“aud”);
String sub = (String) data.get(“sub”);

5.将”identityToken”和上一步解析出来的”kid”,“aud”,”sub”传入一下方法验证JWT是否有效,返回true即等于没问题:

AUTH_TIME_STR = auth_time
public boolean verify(String identityToken,String iss,String aud,String sub) {
PublicKey publicKey = getPublicKey();
JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);
jwtParser.requireIssuer(iss);
jwtParser.requireAudience(aud);
jwtParser.requireSubject(sub);
try {
Jws<Claims> claim = jwtParser.parseClaimsJws(identityToken);
if (null != claim && claim.getBody().containsKey(AUTH_TIME_STR)) {
return true;
}
return false;
} catch (ExpiredJwtException e) {
return false;
} catch (Exception e) {
return false;
}
}

6.没问题的话将userID保存数据库(本文数据库操作忽略…)