1.復制工具類
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.auth0.jwk.Jwk; import io.jsonwebtoken.*; import org.apache.commons.codec.binary.Base64; import org.springframework.web.client.RestTemplate; import java.security.PublicKey; public class AppleUtil { /** * 獲取蘋果的公鑰 * @return * @throws Exception */ private static JSONArray getAuthKeys() throws Exception { String url = "https://appleid.apple.com/auth/keys"; RestTemplate restTemplate = new RestTemplate(); JSONObject json = restTemplate.getForObject(url,JSONObject.class); JSONArray arr = json.getJSONArray("keys"); return arr; } public static Boolean verify(String jwt) throws Exception{ JSONArray arr = getAuthKeys(); if(arr == null){ return false; } JSONObject authKey = null; //先取蘋果第一個key進行校驗 authKey = JSONObject.parseObject(arr.getString(0)); if(verifyExc(jwt, authKey)){ return true; }else{ //再取第二個key校驗 authKey = JSONObject.parseObject(arr.getString(1)); return verifyExc(jwt, authKey); } } /** * 對前端傳來的identityToken進行驗證 * @param jwt 對應前端傳來的 identityToken * @param authKey 蘋果的公鑰 authKey * @return * @throws Exception */ public 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) { 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) { return false; } catch (Exception e) { return false; } } /** * 對前端傳來的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; } }
2.編寫業務邏輯代碼
public AuthUser appleLogin(String identityToken){ String randomStr = UUID.randomUUID().toString(); try { //驗證identityToken if(!AppleUtil.verify(identityToken)){ throw new RuntimeException("identityToken驗證失敗"); } //對identityToken解碼 JSONObject json = AppleUtil.parserIdentityToken(identityToken); if(json == null){ throw new RuntimeException("identityToken解碼失敗"); } //蘋果用戶唯一標識 String appleId = new MD5().digestHex16(json.get("sub").toString()) ; SysUser user = baseMapper.selectOne(new MpUtil<SysUser>().queryNormal().eq(SysUser.APPLE_ID,appleId)); //判斷該賬戶是否被禁用 if(user!=null && user.getDataActive().equals(StatusEnum.STATUS_DISABLE.getStrCode())){ throw new RuntimeException("該賬戶已被禁用,請聯系管理員!"); } //授權綁定過直接獲取token if(user!=null){ //非密碼登錄設置密碼為空 user.setPassword(null); return getToken(user); //把蘋果的登錄唯一標識存儲redis中 }else { RedisUtil.addDataSecond(StrUtil.format(APPLE_AUTH_LOGIN, randomStr),appleId,600l); AuthUser authUser = new AuthUser(); //把鍵存入這個字段 authUser.setAccessToken(randomStr); return authUser; } }catch (Exception e){ throw new RuntimeException("系統錯誤!請聯系管理員"); } }