1、需求描述

APP需要提交苹果的App Store审核时,因为集成了微信授权登录,导致审核失败了,审核失败的理由是:集成了第三方授权的APP,也需要集成Apple授权认证,不然审核通过不了,强制捆绑销售了呀。没办法,胳膊拧不过大腿,还是老老实实的去加了Apple授权认证了。

2、APP授权认证

请参考官方文档:

使用 Apple 登录实现用户身份验证 :

Apple Developer Documentation

在您的应用程序中使用 Apple 按钮显示登录 :

Apple Developer Documentation

实现的效果:

登录界面展示苹果授权登录的按钮,点击授权登录后,输入密码或者指纹授权后,获取 identityToken ---

一个 JSON 网络令牌 (JWT),可安全地将有关用户的信息传达给应用程序

3、后端实现授权认证登录(基于identityToken的方式)

认证的设计流图:

引入maven依赖包:

        <!-- JWT工具库 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>

登录验证参数设置:

import java.io.Serializable;import javax.validation.constraints.NotEmpty;import org.apache.commons.lang3.builder.ToStringBuilder;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;@ApiModel("apple授权登录DTO")
public class AppleLoginDTO implements Serializable {private static final long serialVersionUID = 1L;@ApiModelProperty(value = "apple授权登录码", required = true)@NotEmpty(message = "apple授权登录码不能为空")private String identityToken;public String getIdentityToken() {return identityToken;}public void setIdentityToken(String identityToken) {this.identityToken = identityToken;}@Overridepublic String toString() {return ToStringBuilder.reflectionToString(this);}
}

参数配置:

#apple登录配置
apple:auth:url: https://appleid.apple.com/auth/keysiss:url: https://appleid.apple.com

后端实现的授权认证逻辑:


import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.Objects;import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sisensing.cgm.common.core.domain.TokenUserInfo;
import com.sisensing.cgm.common.core.web.domain.ResponseResult;
import com.sisensing.cgm.common.dto.user.po.AppUserTokenPO;
import com.sisensing.cgm.user.api.domain.dto.AppleLoginDTO;
import com.sisensing.cgm.user.service.IAppleService;import io.jsonwebtoken.*;@Service
public class AppleServiceImpl implements IAppleService {private static final Logger LOGGER = LoggerFactory.getLogger(AppleServiceImpl.class);@Value("${apple.auth.url}")private String appleAuthUrl;@Value("${apple.iss.url}")private String appleIssUrl;@Overridepublic ResponseResult<TokenUserInfo<AppUserTokenPO>> appleLogin(AppleLoginDTO appleLoginDTO) {String identityToken = appleLoginDTO.getIdentityToken();System.out.println("in ");// 获取秘钥的返回信息String firstDate = null;String claim = null;try {firstDate = new String(Base64.decodeBase64(identityToken.split("\\.")[0]), "UTF-8");claim = new String(Base64.decodeBase64(identityToken.split("\\.")[1]), "UTF-8");} catch (UnsupportedEncodingException e) {LOGGER.error("apple授权码异常,identityToken[%s], %s", identityToken, e);}// 开发者帐户中获取的 10 个字符的标识符密钥String kid = JSONObject.parseObject(firstDate).get("kid").toString();String aud = JSONObject.parseObject(claim).get("aud").toString();String sub = JSONObject.parseObject(claim).get("sub").toString();PublicKey publicKey = this.getPublicKey(kid);if (Objects.isNull(publicKey)) {throw new RuntimeException("apple授权登录的数据异常");}boolean reuslt = this.verifyAppleLoginCode(publicKey, identityToken, aud, sub);if (reuslt) {}return null;}private boolean verifyAppleLoginCode(PublicKey publicKey, String identityToken, String aud, String sub) {boolean result = false;JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);jwtParser.requireIssuer(appleIssUrl);jwtParser.requireAudience(aud);jwtParser.requireSubject(sub);try {Jws<Claims> claim = jwtParser.parseClaimsJws(identityToken);if (claim != null && claim.getBody().containsKey("auth_time")) {result = true;}} catch (ExpiredJwtException e) {throw new RuntimeException(String.format("apple登录授权identityToken过期.", e));} catch (SignatureException e) {throw new RuntimeException(String.format("apple登录授权identityToken非法.", e));}return result;}private PublicKey getPublicKey(String kid) {try {CloseableHttpClient httpclient = HttpClientBuilder.create().build();URIBuilder uriBuilder = new URIBuilder(appleAuthUrl);HttpGet httpGet = new HttpGet(uriBuilder.build());HttpResponse response = httpclient.execute(httpGet);int statusCode = response.getStatusLine().getStatusCode();HttpEntity responseEntity = response.getEntity();String result = EntityUtils.toString(responseEntity, "UTF-8");if (statusCode != HttpStatus.SC_OK) { // code = 200throw new RuntimeException(String.format("接口请求失败,url[%s], result[%s]", appleAuthUrl, result));}// 请求成功JSONObject content = JSONObject.parseObject(result);String keys = content.getString("keys");JSONArray keysArray = JSONObject.parseArray(keys);if (keysArray.isEmpty()) {return null;}for (Object key : keysArray) {JSONObject keyJsonObject = (JSONObject)key;if (kid.equals(keyJsonObject.getString("kid"))) {String n = keyJsonObject.getString("n");String e = keyJsonObject.getString("e");BigInteger modulus = new BigInteger(1, Base64.decodeBase64(n));BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(e));RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);KeyFactory kf = KeyFactory.getInstance("RSA");return kf.generatePublic(spec);}}} catch (Exception ex) {LOGGER.error("获取PublicKey异常.", ex);}return null;}
}

ps: 认证授权的代码省略了,具体实现不复杂,认证授权通过后:

            if (claim != null && claim.getBody().containsKey("auth_time")) {result = true;}

就可以去实现系统的业务授权流程,比如首次注册用户的判断呀,或者已经注册的用户,直接返回登录的token等等

4、断点验证截图

5、总结

Apple授权认证的调试过程中,遇到了不少问题,苹果的官方文档对接口的返回字段解释的不太清楚,比如:sub字段,官方文档翻译成中文是:受众注册声明标识了身份令牌所针对的接收者,没有详细说明该值的作用,以及是否会改变,所以暂时当成保留字段来使用;

App获取的 let userIdentifier = appleIDCredential.user 为空,所以后端认证通过后,若是Apple首次授权登录,还需要将appleId返回给App开发,appleID --- 注册苹果账户的邮箱

业务上没有要求做授权时间的控制,所以没有使用授权认证中的 过期时间

JAVA集成apple授权认证登录【后端认证授权】相关推荐

  1. 微信授权(前后端分离授权)

    关于网页授权的微信开发文档地址: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 微信授权分为以下四步: 1 ...

  2. uni-app授权第三方登录(微信授权登录)

    APP微信授权登录需要到微信开放平台申请,在HBuilderX配置APP SDK中微信登录的appId,另外需要跟小程序.公众号授权账号互通的话也需要在微信开放平台申请,这是需要付费的呦(300元) ...

  3. mysql8 授权远程登录_MySQL8 远程授权访问

    在测试环境,想实现MySQL 8 数据库的远程访问授权. 传统的方法已经不行,报1064错误 GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED ...

  4. 微信第三方登录(静默授权和非静默授权)

    用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑. 微信的授权登录在日常应用中应用的非常广泛,越来越多的平台支持用户使用微信进行授权第三方登录 使 ...

  5. Java实现微信扫码登录并实现认证授权

    Java实现微信扫码登录并实现认证授权 1.登录流程及原理 1.1 OAuth2协议 网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统. 在进行微信OAuth2.0 ...

  6. 【49.Auth2.0认证与授权过程-微博开放平台认证授权过程-百度开放平台认证授权过程-社交登录实现(微博授权)-分布式Session问题与解决方案-SpringSession整合-Redis】

    一.知识回顾 [0.三高商城系统的专题专栏都帮你整理好了,请点击这里!] [1-系统架构演进过程] [2-微服务系统架构需求] [3-高性能.高并发.高可用的三高商城系统项目介绍] [4-Linux云 ...

  7. Java面试指北!13个认证授权常见面试题/知识点总结!| JavaGuide

    大家好,我是 Guide哥!端午已过,又要开始工作学习啦! 我发现有很多小伙伴对认证授权方面的知识不是特别了解,搞不清 Session 认证.JWT 以及 Cookie 这些概念. 所以,根据我根据日 ...

  8. 《Spring Boot 实战派》--10.集成安全框架,实现安全 认证和授权

    第10章 集成安全框架,实现安全 认证和授权 本章首先介绍如何使用Spring Security创建独立验证的管理员权限系统.会员系统,讲解如 何进行分表.分权限' 分登录入口.分认证接口.多注册接口 ...

  9. java 认证和授权_SpringSecurity一:认证和授权

    什么是认证? 认证就是取得合法身份,比如京东需要用户登录以后才能才能下订单,这里的登录就是认证.登录成功以后就具有了合法身份可以继续进行下一步的操作. 常用的认证方式 常用的认证方式有基于sessio ...

最新文章

  1. 一致代价搜索_谷歌工程师发布最新技术ScaNN:可实现高效的向量相似性搜索
  2. LVM逻辑卷 (概述lvm,管理卷组 物理卷 逻辑卷,磁盘配额)
  3. JVM003_属性表
  4. 【MySql】MySql存储,游标,循环的简单使用
  5. python中stacked_python – Django管理员StackedInline定制
  6. android leaks工具,Androidx中ImmLeaksCleaner已经解决了InputMethodManager... - 简书
  7. 安卓10源码开发定制(21)GPS定位研究(3)修改GPS定位数据测试gps定位代码
  8. iOS 元素定位方式总结
  9. day01语法、变量、标识符、强制转换笔记
  10. Excel冻结首行/首列
  11. SAP 订单结算方式
  12. 给大家推荐一本书:你只是看起来很努力
  13. 【知识笔记】Debugging
  14. 依图科技创始人:如何对AI技术和产业发展更全面的理解?
  15. 两张图看清英伟达RTX 20系列显卡的新变化
  16. Python编程之四书五经
  17. vb.net mysql 查询,mysql-vb.net查询以显示数据表的特定行[基本]
  18. js数组计算假设一张纸为0.01米 对折多少次后能比珠穆朗玛峰高
  19. D15-Acwing-3.2-4.1代码实现
  20. 风向标瑞福进取昨发疯,真的转势了

热门文章

  1. 好用的 HTTP模块SuperAgent
  2. 牛奶布丁,我也会做了-o-
  3. 初级php工程师掌握技能,php工程师必须掌握的职业技能(上)
  4. 【NOWCODER】- Python:输入输出
  5. 二手车Task3 特征工程
  6. css圆角边框(css圆角边框无效)
  7. 连续5个涨停后,乐视网停牌核查
  8. Vue @click.stop 与 @mousewheel.prevent
  9. 实现串口通信数据帧打包与解析,串口通信可靠传输,屡试不爽的数据封包与状态机数据解析程序
  10. HDU 3974 Assign the task(树 并查集)