apple授权登录(服务端)

一、apple配置

1、注册开发者

在apple开发者官网( https://developer.apple.com/)注册成开发者

2、创建应用

  1. 点击左边菜单的Certificates, Identifiers & Profiles
  2. 点击左边的菜单的Identifiers 创建应用(记得开通苹果登录)
  3. 记下创建应用的包名(apple clientId)

3、获取apple teamId

  1. 右上角的账号名后面的编号即apple TeamId

4、获取密钥id和jwt密钥

  1. 在2步骤中的页面点击 keys
  2. 创建新的密钥
  3. 注册密钥成功后即可获得apple kid 和apple jwt key(只能下载一次,文件为.p8格式)

5、记录apple clientId、teamId、kid、jwtKey 后续开发需要使用到

二、apple授权登录时序图

apple授权登录

三、apple授权登录相关官方文档

1、官方文档地址

https://developer.apple.com/documentation/sign_in_with_apple

2、获取公钥地址
  1. 获取公钥地址的文档地址https://developer.apple.com/documentation/sign_in_with_apple/fetch_apple_s_public_key_for_verifying_token_signature
  2. 接口地址
    https://appleid.apple.com/auth/keys
  3. 注意事项
    接口返回的密钥值是固定的,可以适当缓存使用
3、校验token地址
  1. 校验token的文档地址https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
  2. 接口地址
    https://appleid.apple.com/auth/token

四、注意事项

apple登录只有在第一次授权的时候可以获取到用户信息

五、参考代码

  1. 获取apple公钥列表
String GET_PUBLIC_KEY = "https://appleid.apple.com/auth/keys";
ResponseEntity<String> getApplePublicKeyResp = restTemplate.getForEntity(GET_PUBLIC_KEY, String.class);
            String body = getApplePublicKeyResp.getBody();
            JSONObject resultNode =JSONObject.parseObject(body);
            JSONArray keys = resultNode.getJSONArray("keys");
            Map<String, String> applePublicKeyMap = new HashMap<>(4);
            for (int i=0;i<keys.size();i++) {
            JSONObject jsonObject = keys.getJSONObject(i);
            String kid = jsonObject.getString("kid");
            applePublicKeyMap.put(kid , jsonObject.toString);
      		}
  1. 生成客户端密钥
/**
     * 生成客户端密钥,这个密钥有效期为半年
     *
     * @return 密钥
     */
    @SneakyThrows
    private String getClientSecret() {
    	String kid="apple kid";
    	String teamId ="apple teamId";
    	String clientId= "apple clientId";
        String clientSecret = Jwts.builder()
                .setHeaderParam(JwsHeader.ALGORITHM, "ES256")
                .setHeaderParam(JwsHeader.KEY_ID, kid)
                .setIssuer(teamId)
                .setAudience("https://appleid.apple.com")
                .setSubject(clientId)
                .setExpiration(new Date(System.currentTimeMillis()+180*24*60*60*1000L))
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .signWith(SignatureAlgorithm.ES256, getJwtEncryptKey())
                .compact();
        return clientSecret;
    }
    ```java
     /**
     * 生成jwt密钥
     *
     * @return jwt密钥
     */
   @SneakyThrows
    private PrivateKey getJwtEncryptKey() {
        String jwtEncryptKey ="apple jwtKey";
        PEMParser pemParser = new PEMParser(new StringReader(jwtEncryptKey));
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        PrivateKeyInfo object = (PrivateKeyInfo) pemParser.readObject();
        return converter.getPrivateKey(object);
    }
  1. 校验token
        String CHECK_TOKEN_URL = "https://appleid.apple.com/auth/token";
        HttpHeaders reqHeaders = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/x-www-form-urlencoded");
        reqHeaders.setContentType(type);
        MultiValueMap<String, Object> requestMap = new LinkedMultiValueMap<>(5);
        requestMap.add("client_id", clientId);
        requestMap.add("client_secret", getClientSecret());
        requestMap.add("code", token);
        requestMap.add("grant_type", "authorization_code");
        requestMap.add("redirect_uri", null);
        HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(requestMap, reqHeaders);
        ResponseEntity<String> checkResponseEntity = restTemplate.postForEntity(CHECK_TOKEN_URL, request, String.class); 
        String body = checkResponseEntity.getBody();
        JSONObject resultNode =JSONObject.parseObject(body);
        Optional<String> idToken = Optional.ofNullable(resultNode ).map(it -> it.getString("id_token"));
        Claims claims =getClaims(idToken.get());
        String sub = claims.getSubject();
        if (!unionId.equals(sub)) {
            log.error("用户传递的唯一标识非法,unionId is {},request userId is {}", unionId, sub);
        }
  1. 解析校验token中的值
	private Claims  getClaims (String idToken){
        String jwtHeaderEncoded = idToken.split("\\.")[0];
        String jwtHeader = new String(Base64.decodeBase64(jwtHeaderEncoded));
        JSONObject jwtHeaderNode =JSONObject.parseObject(jwtHeader);
        ApplePublicKey applePublicKey = applePublicKeyMap(jwtHeaderNode.getString("kid"));
        PublicKey publicKey = generatePublicKey(applePublicKey.getN(), applePublicKey.getE());
        return Jwts.parser().setSigningKey(publicKey)
                .parseClaimsJws(idToken)
                .getBody();
	}
	private PublicKey generatePublicKey(String n, String e) {
            BigInteger modulus = new BigInteger(1, Base64.decodeBase64(n));
            BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(e));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
            return keyFactory.generatePublic(spec);
    }
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值