| 
					
				 | 
			
			
				@@ -17,6 +17,7 @@ import org.springframework.util.MultiValueMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.springframework.web.client.RestTemplate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import java.io.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.nio.charset.StandardCharsets; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import java.security.Key; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import java.security.KeyFactory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import java.security.PublicKey; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -67,95 +68,91 @@ public class AppleUtil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public void set_APPLE_BUNDLE_IDENTIFIER(String BUNDLE_IDENTIFIER) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         AppleUtil.APPLE_BUNDLE_IDENTIFIER = BUNDLE_IDENTIFIER; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * 获取苹果的公钥 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * @return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * @throws Exception 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 解码identityToken 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 对前端传来的JWT字符串identityToken的第二部分进行解码 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 主要获取其中的aud和sub,aud大概对应ios前端的包名,sub大概对应当前用户的授权的openID 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param identityToken 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @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 static JSONArray getAuthKeys() throws Exception { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//        String url = "https://appleid.apple.com/auth/keys"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        RestTemplate restTemplate = new RestTemplate(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        JSONObject json = restTemplate.getForObject(APPLE_KEYS_URL,JSONObject.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        JSONArray arr = json.getJSONArray("keys"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return arr; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public static JSONObject parserIdentityToken(String identityToken) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String[] arr = identityToken.split("\\."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String firstDate = new String(Base64.decodeBase64(arr[0]), StandardCharsets.UTF_8); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String decode = new String(Base64.decodeBase64(arr[1]), StandardCharsets.UTF_8); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        JSONObject claimObj = JSON.parseObject(decode); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 将第一部分获取到的kid放入消息体中,方便后续匹配对应的公钥使用 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        claimObj.put("kid", JSONObject.parseObject(firstDate).get("kid")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return claimObj; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public static Boolean verify(String jwt) throws  Exception{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        JSONArray arr = getAuthKeys(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if(arr == null){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        //先取苹果第一个key进行校验 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        JSONObject authKey = arr.getJSONObject(0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if(verifyExc(jwt, authKey)){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }else{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            //再取第二个key校验 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            authKey = arr.getJSONObject(1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return verifyExc(jwt, authKey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 根据kid获取对应的苹果公钥 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param kid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public static PublicKey getPublicKey(String kid) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            RestTemplate restTemplate = new RestTemplate(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            JSONObject data = restTemplate.getForObject(APPLE_KEYS_URL, JSONObject.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            assert data != null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            JSONArray jsonArray = data.getJSONArray("keys"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for (Object obj : jsonArray) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Map json = ((Map) obj); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // 获取kid对应的公钥 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (json.get("kid").equals(kid)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    Jwk jwa = Jwk.fromValues(json); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return jwa.getPublicKey(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * 对前端传来的identityToken进行验证 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * @param jwt 对应前端传来的 identityToken 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * @param authKey 苹果的公钥 authKey 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param identityToken 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param jsonObject 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * @return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * @throws Exception 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public static Boolean verifyExc(String jwt, JSONObject authKey) throws Exception { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        Jwk jwa = Jwk.fromValues(authKey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        PublicKey publicKey = jwa.getPublicKey(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public static Boolean verifyExc(String identityToken, JSONObject jsonObject) throws Exception { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String kid = (String) jsonObject.get("kid"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        PublicKey publicKey = getPublicKey(kid); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        String aud = ""; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        String sub = ""; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if (jwt.split("\\.").length > 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); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        jwtParser.requireAudience((String) jsonObject.get("aud")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        jwtParser.requireSubject((String) jsonObject.get("sub")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            Jws<Claims> claim = jwtParser.parseClaimsJws(jwt); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Jws<Claims> claim = jwtParser.parseClaimsJws(identityToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if (claim != null && claim.getBody().containsKey("auth_time")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                System.out.println(claim); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } catch (ExpiredJwtException e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//            log.error("apple identityToken expired", e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//            log.error("apple identityToken illegal", e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public static Boolean verify(String identityToken) throws  Exception{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * 对前端传来的JWT字符串identityToken的第二部分进行解码 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * 主要获取其中的aud和sub,aud大概对应ios前端的包名,sub大概对应当前用户的授权的openID 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * @param identityToken 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     * @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"} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public static JSONObject parserIdentityToken(String identityToken){ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        String[] arr = identityToken.split("\\."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        Base64 base64 = new Base64(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        String decode = new String (base64.decodeBase64(arr[1])); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        String substring = decode.substring(0, decode.indexOf("}")+1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        JSONObject jsonObject = JSON.parseObject(substring); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return  jsonObject; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        JSONObject playloadObj = parserIdentityToken(identityToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Boolean success; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            success = verifyExc(identityToken, playloadObj); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new RuntimeException(e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return success; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * 获取p8文件中的内容 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -163,7 +160,8 @@ public class AppleUtil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private static Key getPrivateKey() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ClassPathResource resource = new ClassPathResource("config/AuthKey_8G9994KW4L.p8"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            //ClassPathResource resource = new ClassPathResource("config/AuthKey_8G9994KW4L.p8"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ClassPathResource resource = new ClassPathResource("config/AuthKey_"+AppleUtil.APPLE_KID+".p8"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             InputStream inputStream = resource.getInputStream(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             Reader reader = new InputStreamReader(inputStream, "utf-8"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             BufferedReader br = new BufferedReader(reader); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -181,7 +179,7 @@ public class AppleUtil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(org.apache.commons.codec.binary.Base64.decodeBase64(sb.toString().replaceAll("\\n", ""))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return factory.generatePrivate(keySpec); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } catch (FileNotFoundException e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            log.error("not find p8 file !=>{}", e.getMessage()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            System.out.println("not find p8 file !=>"+e.getMessage()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             e.printStackTrace(); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -269,11 +267,10 @@ public class AppleUtil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            log.error("revoke token error"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            System.out.println("revoke token error:" + e.getMessage()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 |