You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

服务端验证Sign in with Apple身份令牌时如何获取audience和subject参数

Sign in with Apple 服务端Token校验参数获取说明

你代码中verify方法需要的audience和subject两个参数获取逻辑如下:

  • audience:这是固定配置值,对应你接入Sign in with Apple时在苹果后台配置的应用标识:
    • 原生iOS App接入场景,传你应用的Bundle ID
    • Web/跨平台应用接入场景,传你配置的Services ID
      你示例中解码出来的Token payload里aud字段值为com.skyming.devicemonitor,这个就是你当前应用对应的audience值,直接写到服务端配置里读取即可,不需要从前端请求参数中动态获取。
  • subject:这是苹果为每个用户在你的应用下生成的全局唯一用户ID,对应Token payload里的sub字段,不能提前写死传固定值:
    1. 首次用户登录时,你本地数据库没有存储该用户对应的sub值,此时不能开启subject强校验,否则所有首次登录的请求都会校验失败。你需要先完成签名、签发方、audience、过期时间的基础校验,校验通过后从解析出的Claims中取出sub值,和你本地新建的用户账号做绑定存储。
    2. 后续老用户登录时,你可以根据请求关联的本地用户信息,取出之前存储的该用户绑定的apple sub值,此时再传入做subject强校验,避免Token被冒用到其他账号上。

注意:你当前获取苹果公钥的逻辑存在缺陷,不要直接取JWKSet返回的第一个公钥。需要先解析identityToken的请求头,拿到头里的kid参数,再去JWKSet的公钥列表里匹配和kid值一致的公钥做校验,否则苹果轮换公钥、返回多个公钥时会出现校验失败的问题。

修正后的代码示例

首先调整verify方法,支持subject可选校验:

public void verify(PublicKey key, String jwt, String audience, String subject) {
        JwtParserBuilder parserBuilder = Jwts.parserBuilder()
                .setSigningKey(key)
                .requireIssuer("https://appleid.apple.com")
                .requireAudience(audience);
        // 仅当传入有效subject时才开启sub校验
        if (subject != null && !subject.trim().isEmpty()) {
            parserBuilder.requireSubject(subject);
        }
        JwtParser jwtParser = parserBuilder.build();
        try {
            Jws<Claims> claim = jwtParser.parseClaimsJws(jwt);
            if (claim != null && claim.getBody().containsKey("auth_time")) {
                String userSub = claim.getBody().getSubject();
                // 后续业务逻辑:拿userSub做用户匹配/绑定
            }
        } catch (ExpiredJwtException e) {
            log.error("apple identityToken expired", e);
        } catch (Exception e) {
            log.error("apple identityToken illegal", e);
        }
    }

登录接口调用逻辑调整:

public Response<LoginResponse> loginWithJwt(FortuneAppleUserJwtLoginRequest request) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        String identityToken = request.getIdentityToken();
        // 1. 先解析token header拿到kid,再匹配对应苹果公钥(原逻辑取第一个公钥的问题记得修正)
        AppleUserRestClient client = RetrofitUtil.getAppleIdRetrofitInstance().create(AppleUserRestClient.class);
        Call<JWKSet> call = client.getPublicKey();
        JWKSet jwkSet = call.execute().body();
        // 省略根据kid匹配公钥的逻辑,自行补充即可
        PublicKey publicKey = getPublicKey(n,e);
        
        // 2. 从服务端配置读取固定的audience值
        String APP_AUDIENCE = "com.skyming.devicemonitor";
        // 3. 如果是老用户,这里查库拿到该用户绑定的sub再传入,首次登录传null即可
        verify(publicKey, identityToken, APP_AUDIENCE, null);
        return new Response<>();
    }

内容的提问来源于stack exchange,提问作者Dolphin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 20:42:20