|
@@ -0,0 +1,132 @@
|
|
|
+package com.miaxis.apple.service.impl;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.auth0.jwk.Jwk;
|
|
|
+import com.miaxis.apple.service.IAppleService;
|
|
|
+import com.miaxis.common.exception.CustomException;
|
|
|
+import io.jsonwebtoken.*;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.web.client.RestTemplate;
|
|
|
+import org.apache.tomcat.util.codec.binary.Base64;
|
|
|
+
|
|
|
+import java.security.PublicKey;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class AppleServiceImpl implements IAppleService {
|
|
|
+ @Override
|
|
|
+ public JSONObject getAppleUserInfo(String identityToken) throws Exception {
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>(1);
|
|
|
+
|
|
|
+ //验证identityToken
|
|
|
+ if (!verify(identityToken)) {
|
|
|
+ throw new CustomException("验证identityToken失败");
|
|
|
+ }
|
|
|
+ //对identityToken解码
|
|
|
+ JSONObject json = parserIdentityToken(identityToken);
|
|
|
+ if (json == null) {
|
|
|
+ throw new CustomException("identityToken解码失败");
|
|
|
+ }
|
|
|
+ return json;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 对前端传来的JWT字符串identityToken的第二部分进行解码
|
|
|
+ * 主要获取其中的aud和sub,aud大概对应ios前端的包名,sub大概对应当前用户的授权的openID
|
|
|
+ *
|
|
|
+ * @param identityToken 身份token
|
|
|
+ * @return {"aud":"com.xkj.****","sub":"000***.8da764d3f9e34d2183e8da08a1057***.0***","c_hash":"UsKAuEoI-****","email_verified":"true","auth_time":1574673481,"iss":"https://appleid.apple.com","exp":1574674081,"iat":1574673481,"email":"****@qq.com"}
|
|
|
+ */
|
|
|
+ private JSONObject parserIdentityToken(String identityToken) {
|
|
|
+ String[] arr = identityToken.split("\\.");
|
|
|
+ String decode = new String(Base64.decodeBase64(arr[1]));
|
|
|
+ String substring = decode.substring(0, decode.indexOf("}") + 1);
|
|
|
+ return JSONObject.parseObject(substring);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public Boolean verify(String jwt) throws Exception {
|
|
|
+ JSONArray arr = getAuthKeys();
|
|
|
+ if (arr == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSONObject authKey = null;
|
|
|
+ //先取苹果第一个key进行校验
|
|
|
+ authKey = arr.getJSONObject(0);
|
|
|
+ if (verifyExc(jwt, authKey)) {
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ //再取第二个key校验
|
|
|
+ authKey = arr.getJSONObject(1);
|
|
|
+ return verifyExc(jwt, authKey);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 对前端传来的identityToken进行验证
|
|
|
+ *
|
|
|
+ * @param jwt 对应前端传来的 identityToken
|
|
|
+ * @param authKey 苹果的公钥 authKey
|
|
|
+ * @return
|
|
|
+ * @throws Exception
|
|
|
+ */
|
|
|
+ private static Boolean verifyExc(String jwt, JSONObject authKey) throws Exception {
|
|
|
+
|
|
|
+ Jwk jwa = Jwk.fromValues(authKey);
|
|
|
+ PublicKey publicKey = jwa.getPublicKey();
|
|
|
+
|
|
|
+ String aud = "";
|
|
|
+ String sub = "";
|
|
|
+ if (jwt.split("\\.").length > 1) {
|
|
|
+ System.out.println(jwt.split("\\.")[1]);
|
|
|
+ String claim = new String(Base64.decodeBase64(jwt.split("\\.")[1]));
|
|
|
+ aud = JSONObject.parseObject(claim).get("aud").toString();
|
|
|
+ sub = JSONObject.parseObject(claim).get("sub").toString();
|
|
|
+ }
|
|
|
+ JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);
|
|
|
+ jwtParser.requireIssuer("https://appleid.apple.com");
|
|
|
+ jwtParser.requireAudience(aud);
|
|
|
+ jwtParser.requireSubject(sub);
|
|
|
+
|
|
|
+ try {
|
|
|
+ Jws<Claims> claim = jwtParser.parseClaimsJws(jwt);
|
|
|
+ if (claim != null && claim.getBody().containsKey("auth_time")) {
|
|
|
+ System.out.println(claim);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ } catch (ExpiredJwtException e) {
|
|
|
+ log.error("[AppleServiceImpl.verifyExc] [error] [apple identityToken expired]", e);
|
|
|
+ return false;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[AppleServiceImpl.verifyExc] [error] [apple identityToken illegal]", e);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取苹果的公钥
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private static JSONArray getAuthKeys() {
|
|
|
+ String url = "https://appleid.apple.com/auth/keys";
|
|
|
+ RestTemplate restTemplate = new RestTemplate();
|
|
|
+ JSONObject json = restTemplate.getForObject(url, JSONObject.class);
|
|
|
+ if (json != null) {
|
|
|
+ return json.getJSONArray("keys");
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+}
|