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

先吐槽一下!苹果的开发文档简直就是一坨S

好了!!开发步骤如下:
1.导入JWT Maven 坐标

<dependency>
     <groupId>io.jsonwebtoken</groupId>
     <artifactId>jjwt</artifactId>
     <version>0.9.0</version>
</dependency>

2.闭上眼睛,以GET方式不带参数访问 https://appleid.apple.com/auth/keys。获取最新JSON,对我们有用的就是"n"和"e",组成JWT公钥(此处官方有变更,现在获取到的keys数组为多个,需要根据下面第三步IOS端获取的identityToken中的kid来找对应下标的keys数组)。

获取到的json内容:
{
  "keys": [
    {
      "kty": "RSA",
      "kid": "AIDOPK1",
      "use": "sig",
      "alg": "RS256",
      "n": "(可能每次都不一样)xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "e": "AQAB"
    }
  ]
}

ps.我这里使用的是SpringBoot RestTemplate的GET请求方式,然后根据上面的"n"和"e"组成公钥:

  • AUTH_DOMAIN = https://appleid.apple.com/auth/keys
  • AppleKeys:请求返回数据的对象
  • RSA = RSA
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.后台取到IOS客户端传过来的 identityToken 是jwt格式(固定三段式以“.”切割…我们只需要取第二段即可)。然后使用Base64解析出来,里面对我们有用的数据只有三个字段:

"iss"=https://appleid.apple.com(固定签名)(**此字段已被官方替换成 “kid” 用于找到第二步中kid匹配的keys**);
"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("kid");
String aud = (String) data.get("aud");
String sub = (String) data.get("sub");

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

  • AUTH_TIME_STR = auth_time
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.没问题的话将userID保存数据库(本文数据库操作忽略…)

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 29
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值