| 
					
				 | 
			
			
				@@ -5,11 +5,23 @@ import com.alibaba.fastjson.JSONArray; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.alibaba.fastjson.JSONObject; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import com.auth0.jwk.Jwk; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import io.jsonwebtoken.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import io.jsonwebtoken.impl.DefaultJwtBuilder; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import lombok.extern.slf4j.Slf4j; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.apache.commons.codec.binary.Base64; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.beans.factory.annotation.Value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.core.io.ClassPathResource; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.http.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.util.MultiValueMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import org.springframework.web.client.RestTemplate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.io.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.security.Key; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.security.KeyFactory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import java.security.PublicKey; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.security.spec.EncodedKeySpec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.security.spec.PKCS8EncodedKeySpec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.HashMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.Map; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * @author:slambb 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -18,6 +30,22 @@ import java.security.PublicKey; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 @Slf4j 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 public class AppleUtil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Value("${apple.KEYS_URL}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static String APPLE_KEYS_URL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    //Revoke tokens 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    //由于苹果注册后,删除用户需要revoke tokens 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public static String privateKeyStr; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Value("${apple.REVOKE_TOKENS_URL}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static String APPLE_REVOKE_TOKENS_URL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Value("${apple.AUTH_TOKENS_URL}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static String APPLE_AUTH_TOKENS_URL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Value("${apple.TEAM_ID}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static String APPLE_TEAM_ID; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Value("${apple.KID}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static String APPLE_KID; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Value("${apple.BUNDLE_IDENTIFIER}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static String APPLE_BUNDLE_IDENTIFIER; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * 获取苹果的公钥 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -25,9 +53,9 @@ public class AppleUtil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      * @throws Exception 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     private static JSONArray getAuthKeys() throws Exception { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        String url = "https://appleid.apple.com/auth/keys"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//        String url = "https://appleid.apple.com/auth/keys"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         RestTemplate restTemplate = new RestTemplate(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        JSONObject json = restTemplate.getForObject(url,JSONObject.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        JSONObject json = restTemplate.getForObject(APPLE_KEYS_URL,JSONObject.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         JSONArray arr = json.getJSONArray("keys"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return arr; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -107,4 +135,125 @@ public class AppleUtil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return  jsonObject; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 获取p8文件中的内容 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static Key getPrivateKey() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ClassPathResource resource = new ClassPathResource("static/config/AuthKey_8G9994KW4L.p8"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            InputStream inputStream = resource.getInputStream(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Reader reader = new InputStreamReader(inputStream, "utf-8"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            BufferedReader br = new BufferedReader(reader); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            String string = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            StringBuffer sb = new StringBuffer(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            while ((string = br.readLine()) != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (string.startsWith("---")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                sb.append(string); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            br.close(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            KeyFactory factory = KeyFactory.getInstance("EC"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            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()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            e.printStackTrace(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 私钥加密后给苹果去验证,构造clientSecret,就是构造一个jwt字符串 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * [获取私钥] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param iss team_id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param sub client_id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param kid access_token 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public static String buildJwt(String iss, String sub, String kid) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Map<String, Object> header = new HashMap<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        header.put("alg", SignatureAlgorithm.ES256.getValue()); //SHA256withECDSA 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        header.put("kid", kid); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        long iat = System.currentTimeMillis() / 1000; //以秒为单位 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Map<String, Object> claims = new HashMap<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        claims.put("iss", iss);// apple开发组id 问ios开发要 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        claims.put("iat", iat); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        claims.put("exp", iat + 180 * 3600); //设置过期时间 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        claims.put("aud", "https://appleid.apple.com"); //固定值 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        claims.put("sub", sub);// Bundle Identifier 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return new DefaultJwtBuilder().setHeader(header).setClaims(claims).signWith(SignatureAlgorithm.ES256, getPrivateKey()).compact(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 用户授权获取 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param code 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public static String getAuthToken(String code) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (code.isEmpty() ) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//        JSONObject jsonObject = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            privateKeyStr = buildJwt(APPLE_TEAM_ID, APPLE_BUNDLE_IDENTIFIER, APPLE_KID); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Map<String, String> stringStringHashMap = new HashMap<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            stringStringHashMap.put("client_id", APPLE_BUNDLE_IDENTIFIER); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            stringStringHashMap.put("client_secret", privateKeyStr); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            stringStringHashMap.put("code", code); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            stringStringHashMap.put("grant_type", "authorization_code"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            HttpHeaders headers = new HttpHeaders(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            HttpEntity request = new HttpEntity<>(stringStringHashMap,headers); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            RestTemplate restTemplate = new RestTemplate(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            JSONObject jsonObject = restTemplate.postForObject(APPLE_AUTH_TOKENS_URL,request,JSONObject.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return jsonObject.get("access_token").toString(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new RuntimeException(e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public static Boolean appleRevoke(String code) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            String authToken = getAuthToken(code); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Map<String, String> requestMap = new HashMap<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            requestMap.put("client_id", APPLE_BUNDLE_IDENTIFIER); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            requestMap.put("client_secret", privateKeyStr); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            requestMap.put("token", authToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            requestMap.put("token_type_hint", "access_token"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            System.out.println(requestMap); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            HttpHeaders headers = new HttpHeaders(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            HttpEntity request = new HttpEntity<>(requestMap,headers); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            RestTemplate restTemplate = new RestTemplate(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ResponseEntity<String> responseEntity = restTemplate.postForEntity(APPLE_REVOKE_TOKENS_URL,request,String.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            int statusCode = responseEntity.getStatusCodeValue(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (statusCode != 200) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                JSONObject jsonObject = JSON.parseObject(responseEntity.getBody()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                String error = jsonObject.get("error").toString(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                System.out.println("error --------- " + error); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (!error.isEmpty()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } catch (Exception e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            log.error("revoke token error"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 |