maven依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>jwks-rsa</artifactId>
<version>0.9.0</version>
</dependency>
java工具类
/**
* 苹果登录工具类
* @author qinweipeng
* @Description
* @create 2020-07-23 15:18
*/
public class AppleLoginUtil {
/**
* 获取苹果公钥
* 此处可能返回多个公钥,根据identifyToken中header中的kid来定位并生成对应的publicKey
* @return
*/
public static List<ApplePublicKey> getApplePublicKey() {
String httpResult = new String(HttpsUtil.doGet("https://appleid.apple.com/auth/keys"));
if (StringUtils.isBlank(httpResult)) {
throw new BusinessException("获取公钥失败");
}
AppleAuthKeysResponse appleAuthKeysResponse = JsonUtil.fromJson(httpResult, AppleAuthKeysResponse.class);
return appleAuthKeysResponse.getKeys();
}
/**
* 生成公钥
* @param applePublicKey ApplePublicKey 转map
* @return
* @throws InvalidPublicKeyException
*/
public static PublicKey genPublicKey(Map<String, Object> applePublicKey) throws InvalidPublicKeyException {
Jwk jwa = Jwk.fromValues(applePublicKey);
return jwa.getPublicKey();
}
/**
* 验证
* @param key
* @param jwt
* @param audience
* @param subject
* @return
*/
public static boolean verify(PublicKey key, String jwt, String audience, String subject) {
JwtParser jwtParser = Jwts.parser().setSigningKey(key);
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 true;
}
return false;
} catch (ExpiredJwtException e) {
throw new BusinessException("苹果token过期", e);
} catch (Exception e) {
throw new BusinessException("非法token", e);
}
}
/**
* 验证token
* @return 苹果用户唯一编码
*/
public static String verifyIdentityToken(String identityToken){
if (identityToken.split("\\.").length <= 1) {
throw new BusinessException("不合法的token");
}
List<ApplePublicKey> applePublicKeys = getApplePublicKey();
Map<String, List<ApplePublicKey>> applePublicKeyMap = applePublicKeys.stream().collect(Collectors.groupingBy(ApplePublicKey::getKid));
String headStr = new String(Base64.decodeBase64(identityToken.split("\\.")[0]));
String claimStr = new String(Base64.decodeBase64(identityToken.split("\\.")[1]));
AppleTokenHead head = JsonUtil.fromJson(headStr, AppleTokenHead.class);
AppleTokenClaims claims = JsonUtil.fromJson(claimStr, AppleTokenClaims.class);
List<ApplePublicKey> publicKeys = applePublicKeyMap.get(head.getKid());
if (CollectionUtils.isEmpty(publicKeys)) {
throw new BusinessException("未获取到合法密钥,登录失败");
}
ApplePublicKey publicKey = publicKeys.get(0);
Map<String, Object> map = (Map<String, Object>) JsonUtil.jsonToMap(JsonUtil.objectToJson(publicKey));
try {
PublicKey key = genPublicKey(map);
if (verify(key, identityToken, claims.getAud(), claims.getSub())) {
return claims.getSub();
} else {
throw new BusinessException("token验证失败");
}
} catch (InvalidPublicKeyException e) {
throw new BusinessException("不合法的密钥", e);
}
}
}
用到的bean:
@Data
public class AppleAuthKeysResponse {
private List<ApplePublicKey> keys;
}
@Data
public class ApplePublicKey {
private String kty;
private String kid;
private String use;
private String alg;
private String n;
private String e;
}
@Data
public class AppleTokenClaims {
/**
* 苹果签发
*/
private String iss;
/**
* APP ID
*/
private String aud;
private String exp;
private String iat;
/**
* 用户唯一标识
*/
private String sub;
private String c_hash;
private String auth_time;
}
@Data
public class AppleTokenHead {
/**
* 对应密钥中的kid
*/
private String kid;
/**
* 加密算法
*/
private String alg;
}
http工具类、json工具类、自定义异常等请自行处理;