背景:

项目中app登录有第三方账号授权登录,在今年6月30号以后,苹果对发布的app中若有第三方账号授权,必须得接入苹果账号的授权登录的硬性要求。不然在发版时,会不通过审核


苹果授权流图

代码实现

maven的依赖:

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

通过RestTemplate发起请求获取苹果的公钥信息:

public PublicKey getPublicKey(String kid) {logger.debug("get public key start,kid={}", kid);try {HeaderMap<String, Object> headerMap = new HeaderMap<>();headerMap.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PROBLEM_JSON_UTF8_VALUE);headerMap.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_UTF8_VALUE);AppleKeyResponse appleKeyResponse = get(GET_KEY_URL, headerMap, new ResponseType<AppleKeyResponse>() {});logger.debug("get public key success,appleKeyResponse={}", appleKeyResponse);if (null == appleKeyResponse || CollectionUtils.isEmpty(appleKeyResponse.getKeys())){return null;}List<AppleKeyVo> appleKeyVos = appleKeyResponse.getKeys();for (AppleKeyVo appleKeyVo : appleKeyVos) {if (kid.equals(appleKeyVo.getKid())) {BigInteger modulus = new BigInteger(1, Base64.decodeBase64(appleKeyVo.getN()));BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(appleKeyVo.getE()));RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);KeyFactory kf = KeyFactory.getInstance("RSA");return kf.generatePublic(spec);}}} catch (Exception e) {logger.error("get public key fail,e={}", e);}return null;}

注意
1、GET_KEY_URL =https://appleid.apple.com/auth/keys
2、get(GET_KEY_URL, headerMap, new ResponseType() {});这里的get方法,是公司封装RestTemplate的get请求方法。
3、获取到苹果公钥是一个数组,需要根据从前端传过来的identityToken中解析对应的kid(密钥id),如何解析,参考下面的解析identityToken代码


AppleKeyResponse 类

 public class AppleKeyResponse {private List<AppleKeyVo> keys;public List<AppleKeyVo> getKeys() {return keys;}public void setKeys(List<AppleKeyVo> keys) {this.keys = keys;}@Overridepublic String toString() {return "AppleKeyResponse{" +"keys=" + keys +'}';}

AppleKeyVo 类

 public class AppleKeyVo {/*** 加密算法*/private String kty;/*** 密钥id*/private String kid;/*** 用处*/private String use;/*** 算法*/private String alg;/*** 公钥参数*/private String n;/*** 公钥参数*/private String e;public String getKty() {return kty;}public void setKty(String kty) {this.kty = kty;}public String getKid() {return kid;}public void setKid(String kid) {this.kid = kid;}public String getUse() {return use;}public void setUse(String use) {this.use = use;}public String getAlg() {return alg;}public void setAlg(String alg) {this.alg = alg;}public String getN() {return n;}public void setN(String n) {this.n = n;}public String getE() {return e;}public void setE(String e) {this.e = e;}@Overridepublic String toString() {return "AppleKeyVo{" +"kty='" + kty + '\'' +", kid='" + kid + '\'' +", use='" + use + '\'' +", alg='" + alg + '\'' +", n='" + n + '\'' +", e='" + e + '\'' +'}';}

解析identityToken(是苹果账号生成的授权码)

/*** 苹果工具类** @author :  * @version : 1.0.0* @date :   2020/7/13 17:10*/
public class AppleUtil {private static Logger logger = LoggerFactory.getLogger(AppleUtil.class);private static final String AUTH_TIME = "auth_time";private static final String APPLE_SERVER_ULR = "https://appleid.apple.com";/*public static void main(String[] args) {AppleTokenVo appleTokenVo = decodeIdentityToken("eyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmNoYW5nZGFvLnR0c2Nob29sIiwiZXhwIjoxNTg5MjcwMzI3LCJpYXQiOjE1ODkyNjk3MjcsInN1YiI6IjAwMTk0MC43YTExNDFhYTAwMWM0NjllYTE1NjNjNmJhZTk5YzM3ZC4wMzA3IiwiY19oYXNoIjoienNIUW9xbTdjcDZOcmxrUHFhTmpGQSIsImVtYWlsIjoiYXEzMmsydnpjd0Bwcml2YXRlcmVsYXkuYXBwbGVpZC5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJpc19wcml2YXRlX2VtYWlsIjoidHJ1ZSIsImF1dGhfdGltZSI6MTU4OTI2OTcyNywibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.q5unOzswOjpRYmrVKVm3FRb_Th6kkhgEvoFfTEAIETwgTXZ7bYcQM8J8tCjkGGqtt2z74Z-wTW7Q3ia209xhmwrVDIup0jcQgNTvsCEMkfo9evPIDrNRNQw2Dzw2EBKma8004NL6THYlySoDnPRoW_VQCHP_m0HnjYuIc-wtREEClf-_tOFDPpTsvUFoETHNfhpsLhqj24-zm6MSOocYY3WbUaYJQVEFCz-x6AGko1XkMtms_-JU1xakNtjMZTIVj2XyUI5MO7_eo-D9i_c7Hj-OE9HNBEvFnPxOesDzXvEoYdb7uByXEfa-H1syJMecBMRa3tL76W_CYKsONRkU9Q");System.out.println(appleTokenVo);}*//*** 获取publicKey 的算法id** @param identityToken 苹果token的第一部分* @return String*/public static String getKid(String identityToken) {String kid = null;try {String str1 = new String(Base64.decodeBase64(identityToken), StandardCharsets.UTF_8);Map<String, Object> data1 = JSONUtil.readValue(str1, new TypeReference<Map<String, Object>>() {});kid = (String) data1.get("kid");} catch (Exception e) {logger.error("get kid fail,e={}", e);}return kid;}/*** 解密个人信息** @param identityToken APP获取的identityToken的第二部分* @return 解密参数:失败返回null  sub就是用户id,用户昵称需要前端传过来*/public static AppleTokenVo getAppleUserInfo(String identityToken) {AppleTokenVo appleTokenVo = null;try {String str2 = new String(Base64Utils.decodeFromString(identityToken), StandardCharsets.UTF_8);appleTokenVo = JSONUtil.readValue(str2, AppleTokenVo.class);} catch (Exception e) {logger.info("get apple user information fail,e={} ", e);}return appleTokenVo;}/*** 验证** @param identityToken APP获取的identityToken* @param aud           您在您的Apple Developer帐户中的client_id* @param sub           用户的唯一标识符对应APP获取到的:user* @return true/false*/public static boolean verifyIdentityToken(PublicKey publicKey, String identityToken, String aud, String sub) {try {JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);jwtParser.requireIssuer(APPLE_SERVER_ULR);jwtParser.requireAudience(aud);jwtParser.requireSubject(sub);Jws<Claims> claim = jwtParser.parseClaimsJws(identityToken);if (claim != null && claim.getBody().containsKey(AUTH_TIME)) {return true;}} catch (ExpiredJwtException e1) {logger.error("apple token verify fail,identityToken is expired!");} catch (Exception e2) {logger.error("apple token verify fail,error={}", e2);}return false;}}

AppleTokenVo类(identityToken解析出来的信息):

 /*** 苹果用户token中信息** @version : 1.0.0* @date :   2020/7/17 15:03*/
public class AppleTokenVo {/*** 签发机构网址*/private String iss;/*** bundle id*/private String aud;/*** 过期时间戳*/private Long exp;/*** 签发时间*/private Long iat;/*** user id*/private String sub;/*** 客户端发出请求时携带的随机串,用于对照*/private String nonce;/*** 邮箱*/private String email;public String getIss() {return iss;}public void setIss(String iss) {this.iss = iss;}public String getAud() {return aud;}public void setAud(String aud) {this.aud = aud;}public Long getExp() {return exp;}public void setExp(Long exp) {this.exp = exp;}public Long getIat() {return iat;}public void setIat(Long iat) {this.iat = iat;}public String getSub() {return sub;}public void setSub(String sub) {this.sub = sub;}public String getNonce() {return nonce;}public void setNonce(String nonce) {this.nonce = nonce;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}@Overridepublic String toString() {return "AppleTokenVo{" +"iss='" + iss + '\'' +", aud='" + aud + '\'' +", exp=" + exp +", iat=" + iat +", sub='" + sub + '\'' +", nonce='" + nonce + '\'' +", email='" + email + '\'' +'}';}
}

总结:

苹果授权登录,代码大概就以上这些,除了公司封装RestTemplate,其他都有了。

Java对接苹果账号授权登录相关推荐

  1. 苹果apple账号授权登录第三方APP

    Apple官方文档 前言:由于公司最近有个业务需求是要进行Apple账号授权登录,于是我看边看文档边借鉴其他人的写法,发现好多文章都有一个共性,一个是在解析JWT的时候自己设置参数后进行判断,这样做没 ...

  2. java如何实现微信授权登录

    要在 Java 中实现微信授权登录,需要按照以下步骤操作: 在微信开放平台(https://open.weixin.qq.com/)申请微信登录的第三方平台资格,并获取相应的 AppID 和 AppS ...

  3. iOS小课堂: 集成 《阿里百川》教程( 打开商品详情页、 淘宝账号授权登录、 完成交易闭环)

    文章目录 前言 I 打开商品详情页面 II 淘宝账号授权登录 2.2 集成文档 2.3 Cocoapod方式引入百川SDK 上 III 常见错误 3.2 读取身份图片AppKey失败, see als ...

  4. 苹果申请新专利:多个苹果账号可登录同一台苹果设备

    最近的一个新专利显示Apple正在开发允许一个设备登入多个AppleID的新功能.而此功能亦表示多人共用一部iPhone或iPad时,将可以设置多个密​​码.FaceID等. 对Apple产品用家来说 ...

  5. 干货|JustAuth三方账号授权登录免费搭建全流程

    三方登录的方式想必大家都很熟悉,基本健全的网页都会整几个入口,比如日常的微信.QQ,金融的支付宝,音视频的抖音.快手,码农领域的Github.Gitee等. 作为功能测试,我们就随机取一个简单的三方授 ...

  6. web网页第三方账号授权登录

    ❤️最细微信小程序版本上传.提交审核.发布[建议收藏]❤️ ❤️2021直击大厂前端开发岗位面试题❤️ ❤️效果图如下,如有需要请自取修改[建议收藏]!❤️ ❤️微信小程序的灰度发布❤️ web网页端 ...

  7. 三方账号授权登录系统设计思路

    文章目录 背景 关键概念 系统设计思路 总结 背景 借技术总结时间梳理一下三方登录授权的一些技术细节实现,假设saas 店铺和商品管理插件中心是两个独立的账号体系(内部分别对应 shopId 和 se ...

  8. php拼多多登录下单接口,对接拼多多授权登录及店铺信息接口_tp5

    1 //授权控制器 2 <?php3 namespace app\index\controller;4 usethink\Controller;5 usethink\Db;6 usethink\ ...

  9. 31、Java——JDBC实现账号密码登录

    ✅作者简介:热爱国学的Java后端开发者,修心和技术同步精进.

  10. 利用github进行账号授权登录

    本文用到了OAuth2.0中最复杂的授权码模式,正好拿我这个案例给大家分享一下OAuth2.0的授权过程. 先去github上注册一下身份,附上链接 https://github.com/login/ ...

最新文章

  1. javascript取得鼠标的位置
  2. 基于Java的RDMA高性能通信库(三):Direct Storage and Networking Interface (DiSNI)
  3. win7 nvme 支持补丁_UpdatePack7R2 v20.8.13 Win7 更新补丁包
  4. 2008年CCNA第三学期第一单元中文题目(2008-12-21 18:30:01
  5. 云开发的数据库权限机制解读丨云开发101
  6. linux上听FM程序,安装和使用Odio在Linux上收听FM收音机的方法
  7. linux线程间同步(1)互斥锁与条件变量
  8. 跨考计算机教研室,跨考教研室专家:脱离题海沉浮 做到有效做题_跨考网
  9. BootStrap笔记-Model(模式对话框)样式修改
  10. 49 - 算法 - Leetcode-111 -二叉树的最小深度 -递归循环
  11. 【java】接口(interface)
  12. NLog 2.0.0.2000 使用实例
  13. python编写agent_python实现Agent守护进程
  14. 诺基亚PC套件界面设计
  15. 环形电流计算公式_环形电感的计算公式
  16. 设置notepad++背景护眼色
  17. 兔子、狼、狐狸、王八
  18. FOR 循环 珠峰折纸
  19. android 存储盘 dcim,DCIM是个啥?安卓图片存储位置指南
  20. 泛微OA E9后端环境搭建(IDEA) Ecology 9二次开发环境搭建 ecology二次开发

热门文章

  1. 关于kinfu的配置问题,pcl_kinfu_largeScale
  2. 【计算机图形学】Liang-Barsky裁剪算法(C++实现)
  3. mysql 创建 utf-8 数据库_mysql 创建数据库 utf-8
  4. 大写罗马数字(大写罗马数字3)
  5. java 取系统当前时间_java获取当前系统时间方法
  6. 帝国cms 自动生成html,帝国cms实现用户访问页面自动生成html的方法
  7. 苹果自带的清理软件_苹果清理软件哪个好,哪个更适合自己 - 単子
  8. 《熊出没·原始时代》首映 导演点赞宋祖儿配音
  9. 没有密码,如何取消Word文档的只读模式?
  10. html refresh原理,HTML meta refresh 刷新与跳转(重定向)页面