Apple登录

末蓝、 2023-03-02 04:59 23阅读 0赞

目前的APP在苹果上如果要使用第三方登录就必须实现apple登录,本文在Spring Security的基础上整合apple登录。

苹果文档
没有例子,没有代码,是真的烦。

注意:使用苹果登录时尽量不要在登录后要求用户绑定手机号,尽量实现隐式注册的效果,否则也可能会被驳回。

代码

maven依赖
  1. <dependency>
  2. <groupId>com.auth0</groupId>
  3. <artifactId>jwks-rsa</artifactId>
  4. <version>${ jjwt.version}</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>io.jsonwebtoken</groupId>
  8. <artifactId>jjwt</artifactId>
  9. <version>${ jjwt.version}</version>
  10. </dependency>

我这里的版本号是0.9.0,只是提取到配置里面了。

解析类
  1. import lombok.Data;
  2. @Data
  3. public class Key {
  4. private String kty;
  5. private String kid;
  6. private String use;
  7. private String alg;
  8. private String n;
  9. private String e;
  10. }
token验证工具类

验证方法封装成了一个接口

  1. import java.math.BigInteger;
  2. import java.security.KeyFactory;
  3. import java.security.PublicKey;
  4. import java.security.spec.RSAPublicKeySpec;
  5. import java.util.List;
  6. import org.apache.commons.codec.binary.Base64;
  7. import org.springframework.web.client.RestTemplate;
  8. import com.alibaba.fastjson.JSONObject;
  9. import io.jsonwebtoken.Claims;
  10. import io.jsonwebtoken.ExpiredJwtException;
  11. import io.jsonwebtoken.Jws;
  12. import io.jsonwebtoken.JwtParser;
  13. import io.jsonwebtoken.Jwts;
  14. public class AppleImpl implements Apple {
  15. @Override
  16. public Boolean sign(String openId, String token) {
  17. if (token.split("\\.").length > 1) {
  18. String first = new String(Base64.decodeBase64(token.split("\\.")[0]));
  19. String kId = JSONObject.parseObject(first).get("kid").toString();
  20. String claim = new String(Base64.decodeBase64(token.split("\\.")[1]));
  21. // 官方文档:当前值为开发人员账户中的client_id,我的项目解析出来的是包名
  22. String aud = JSONObject.parseObject(claim).get("aud").toString();
  23. // 对应用户openId(唯一)
  24. String sub = JSONObject.parseObject(claim).get("sub").toString();
  25. try {
  26. // 验证token真实性且解析出的openId==前端申请到的openId
  27. return verify(getPublicKey(kId), token, aud, sub) && openId.equals(sub);
  28. } catch (Exception e) {
  29. }
  30. }
  31. return null;
  32. }
  33. /** * * @param key * 公钥 * @param jwt * token * @param audience * client_id * @param subject * openId */
  34. public static Boolean verify(PublicKey key, String jwt, String audience, String subject) throws Exception {
  35. JwtParser jwtParser = Jwts.parser().setSigningKey(key);
  36. jwtParser.requireIssuer("https://appleid.apple.com");
  37. jwtParser.requireAudience(audience);
  38. jwtParser.requireSubject(subject);
  39. try {
  40. Jws<Claims> claim = jwtParser.parseClaimsJws(jwt);
  41. if (claim != null && claim.getBody().containsKey("auth_time")) {
  42. return true;
  43. }
  44. } catch (ExpiredJwtException e) {
  45. throw new Exception("登录失败");
  46. }
  47. return false;
  48. }
  49. public static PublicKey getPublicKey(String kId) {
  50. try {
  51. RestTemplate restTemplate = new RestTemplate();
  52. String str = restTemplate.getForObject("https://appleid.apple.com/auth/keys", String.class);
  53. JSONObject data = JSONObject.parseObject(str);
  54. List<Key> keys = JSONObject.parseArray(data.getString("keys"), Key.class);
  55. // 返回两个Key,一般第一个就可以解析成功,不成功再使用第二个再次尝试,此处根据kId匹配,可以避免这个情况
  56. for (Key key : keys) {
  57. if (key.getKid().equals(kId)) {
  58. BigInteger modulus = new BigInteger(1, Base64.decodeBase64(key.getN()));
  59. BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(key.getE()));
  60. RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
  61. KeyFactory kf = KeyFactory.getInstance("RSA");
  62. return kf.generatePublic(spec);
  63. }
  64. }
  65. } catch (Exception e) {
  66. }
  67. return null;
  68. }
  69. }

至此基本就可以实现apple登录,至于openId和token的获取方式不同的项目传输方式也不一样,只要能拿到就行了。

Security整合

我这里是老版本的security,同时依赖了social,最新版只需要security就可以,为了稳定暂时没升级。

修改OpenIdAuthenticationProvider 类,可以看我前面的文章了解一下我的代码结构。

思路:
1、判断登陆类型
2、如果是苹果登陆解析token
3、解析成功隐式注册用户、解析失败进入失败处理器

上述部分是纯粹代码逻辑,所以代码就不放了

解决某些项目必须绑定手机号的问题

这种情况发生在token解析成功的时候,在不改动自己原有代码的情况下取巧:
前端传递版本号,将高于当前版本的隐式注册(即送审的版本、送审时设置成手动更新、送审完成后后端关闭隐式注册)
在这里插入图片描述

更优的方法:

合理使用security的角色权限,对于所有的第三方登录全都使用隐式注册,但是没有数据添加权限!!!在需要执行操作时提示绑定手机号授予新权限。

发表评论

表情:
评论列表 (有 0 条评论,23人围观)

还没有评论,来说两句吧...

相关阅读