Java后端校验Sign in With Apple (苹果APP授权登录)

先吐槽!苹果开发文档就是S
好了,开发步骤:
1.导入JWT Maven

<dependency>
     <groupId>io.jsonwebtokengroupId>
     <artifactId>jjwtartifactId>
     <version>0.9.0version>
dependency>

2.首先闭上眼睛,以GET方式不带参数访问 https://appleid.apple.com/auth/keys,获取最新JSON,对我们有用的就是"n"和"e",组成JWT公钥。

获取到的json内容:
{
  "keys": [
    {
      "kty": "RSA",
      "kid": "AIDOPK1",
      "use": "sig",
      "alg": "RS256",
      "n": "lxrwmuYSAsTfn-lUu4goZSXBD9ackM9OJuwUVQHmbZo6GW4Fu_auUdN5zI7Y1dEDfgt7m7QXWbHuMD01HLnD4eRtY-RNwCWdjNfEaY_esUPY3OVMrNDI15Ns13xspWS3q-13kdGv9jHI28P87RvMpjz_JCpQ5IM44oSyRnYtVJO-320SB8E2Bw92pmrenbp67KRUzTEVfGU4-obP5RZ09OxvCr1io4KJvEOjDJuuoClF66AT72WymtoMdwzUmhINjR0XSqK6H0MdWsjw7ysyd_JhmqX5CAaT9Pgi0J8lU_pcl215oANqjy7Ob-VMhug9eGyxAWVfu_1u6QJKePlE-w",
      "e": "AQAB"
    }
  ]
}
我使用restTemplate方式请求,并组成公钥:
public PublicKey getPublicKey() {
        try {
            String forObject = restTemplate.getForObject(AUTH_DOMAIN, String.class);
            if(StringUtils.isEmpty(forObject)){ return null; }
            AppleKeys appleKeys = JsonUtils.parseObject(forObject, AppleKeys.class);
            List<AppleKeys.Keys> keys = appleKeys.getKeys();
            String n = keys.get(0).getN();
            String e = keys.get(0).getE();
            final BigInteger modulus = new BigInteger(1, Base64.decodeBase64(n));
            final BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(e));
            final RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
            final KeyFactory kf = KeyFactory.getInstance(RSA);
            return kf.generatePublic(spec);
        } catch (final Exception e) {
            log.debug("[APPLE]ERROR:{}",e)
        }
        return null;
    }

3.IOS客户端经过一系列操作获取到:identityTokenuserID等。将identityToken传递到后台(其他参数没啥用)

4.后台取到 identityToken 是jwt格式(固定三段式以“.”切割…我们只需要取第二段即可),然后使用Base64解析出来,对我们有用的数据只有三个字段:

"iss"=https://appleid.apple.com(固定签名);
"aud"=APPID
"sub"=**用户的唯一标识**(所以前端不需要额外传递userID)
String [] identityTokens = identityToken.split("\\.");
Map<String, Object> data = JsonUtils.parseMap(new String(Base64.decodeBase64(identityTokens[1]),"UTF-8"),String.class, Object.class);
String iss = (String) data.get("iss");
String aud = (String) data.get("aud");
String sub = (String) data.get("sub");

5.将"identityToken"和上一步解析出来的"iss",“aud”,"sub"传入一下方法验证JWT是否有效,返回true即等于没问题:

public boolean verify(String identityToken,String iss,String aud,String sub)  {
        PublicKey publicKey = getPublicKey();
        JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);
        jwtParser.requireIssuer(iss);
        jwtParser.requireAudience(aud);
        jwtParser.requireSubject(sub);
        try {
            Jws<Claims> claim = jwtParser.parseClaimsJws(identityToken);
            if (null != claim && claim.getBody().containsKey(AUTH_TIME_STR)) {
                return true;
            }
            return false;
        } catch (ExpiredJwtException e) {
            return false;
        } catch (Exception e) {
            return false;
        }
    }

6.ORM操作忽略

你可能感兴趣的:(OAUTH2)