直接上代码
@Data
@ApiModel(value="AppleLoginDto", description = "苹果登录信息")
public class AppleLoginDto {
@ApiModelProperty(value="苹果jwt;必填;",name="identityToken")
public String identityToken;
@ApiModelProperty(value="userId;必填;",name="userId")
public String userId;
@ApiModelProperty(value="email;非必填;",name="email")
public String email;
}
@Service
public class AppleLoginService {
private final String APPLE_AUTH_URL = "https://appleid.apple.com/auth/keys";
private final String ISS = "https://appleid.apple.com";
protected Logger logger = LoggerFactory.getLogger(this.getClass());
public boolean verify(AppleLoginDto appleLoginDto) {
//这里传过来的identityToken应该是三个.分割,解密之后
String identityToken = appleLoginDto.getIdentityToken();
try {
if (identityToken.split("\\.").length > 1){
String firstDate = new String( Base64.decodeBase64(identityToken.split("\\.")[0]),"UTF-8");
String claim = new String(Base64.decodeBase64(identityToken.split("\\.")[1]), "UTF-8");
String kid = JSONObject.parseObject(firstDate).get("kid").toString();
String aud = JSONObject.parseObject(claim).get("aud").toString();
String sub = JSONObject.parseObject(claim).get("sub").toString();
PublicKey publicKey = getPublicKey(kid);
if (publicKey == null) {
logger.error("Apple have no info data!");
return false;
}
boolean reuslt = verify(publicKey, identityToken, aud, sub);
if (reuslt) {
logger.info("苹果登录授权成功");
return true;
}
}
} catch (Exception e) {
logger.error("苹果登录授权异常:", e.getMessage());
e.printStackTrace();
}
logger.error("identityToken格式不正确");
return false;
}
private PublicKey getPublicKey(String kid) {
try {
JSONObject debugInfo = getHttp(APPLE_AUTH_URL);
if (debugInfo == null) {
return null;
}
JSONObject jsonObject = debugInfo.getJSONObject("body");
String keys = jsonObject.getString("keys");
JSONArray jsonArray = JSONObject.parseArray(keys);
if (jsonArray.isEmpty()) {
return null;
}
for (Object object : jsonArray) {
JSONObject json = ((JSONObject) object);
if (json.getString("kid").equals(kid)) {
String n = json.getString("n");
String e = json.getString("e");
BigInteger modulus = new BigInteger(1, Base64.decodeBase64(n));
BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(e));
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
}
} catch (Exception e) {
logger.error("getPublicKey异常!",e.getMessage());
e.printStackTrace();
}
return null;
}
private boolean verify(PublicKey key, String jwt, String audience, String subject){
boolean result = false;
JwtParser jwtParser = Jwts.parser().setSigningKey(key);
jwtParser.requireIssuer(ISS);
jwtParser.requireAudience(audience);
jwtParser.requireSubject(subject);
try {
Jws<Claims> claim = jwtParser.parseClaimsJws(jwt);
if (claim != null && claim.getBody().containsKey("auth_time")) {
return true;
}
} catch (ExpiredJwtException e) {
logger.error("getPublicKey异常{苹果identityToken过期}", e.getMessage());
} catch (Exception e) {
logger.error("getPublicKey异常{苹果identityToken非法}", e.getMessage());
}
return result;
}
private JSONObject getHttp(String url) {
logger.info("[请求地址]: " + url);
JSONObject resultJson = new JSONObject();
resultJson.put("code", -1);
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
HttpGet httpPost = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpPost);
try {
HttpEntity entity = response.getEntity();
if (response.getStatusLine().getStatusCode() == 200) {
String resp = EntityUtils.toString(entity);
resultJson.put("code", 0);
resultJson.put("body", JSONObject.parseObject(resp));
} else {
resultJson.put("code", response.getStatusLine().getStatusCode());
logger.error("[错误码] :" + response.getStatusLine().getStatusCode());
logger.error("[请求地址] :" + url);
}
} finally {
response.close();
}
} catch (ClientProtocolException e) {
logger.error("[异常] :", e.getMessage());
} catch (IOException e) {
logger.error("[异常] :", e.getMessage());
} finally {
try {
httpclient.close();
} catch (IOException e) {
logger.error("[httpclient 关闭异常] : ", e.getMessage());
}
}
return resultJson;
}
}