JAVA集成apple授权认证登录【后端认证授权】
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授权认证登录【后端认证授权】相关推荐
- 微信授权(前后端分离授权)
关于网页授权的微信开发文档地址: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 微信授权分为以下四步: 1 ...
- uni-app授权第三方登录(微信授权登录)
APP微信授权登录需要到微信开放平台申请,在HBuilderX配置APP SDK中微信登录的appId,另外需要跟小程序.公众号授权账号互通的话也需要在微信开放平台申请,这是需要付费的呦(300元) ...
- mysql8 授权远程登录_MySQL8 远程授权访问
在测试环境,想实现MySQL 8 数据库的远程访问授权. 传统的方法已经不行,报1064错误 GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED ...
- 微信第三方登录(静默授权和非静默授权)
用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑. 微信的授权登录在日常应用中应用的非常广泛,越来越多的平台支持用户使用微信进行授权第三方登录 使 ...
- Java实现微信扫码登录并实现认证授权
Java实现微信扫码登录并实现认证授权 1.登录流程及原理 1.1 OAuth2协议 网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统. 在进行微信OAuth2.0 ...
- 【49.Auth2.0认证与授权过程-微博开放平台认证授权过程-百度开放平台认证授权过程-社交登录实现(微博授权)-分布式Session问题与解决方案-SpringSession整合-Redis】
一.知识回顾 [0.三高商城系统的专题专栏都帮你整理好了,请点击这里!] [1-系统架构演进过程] [2-微服务系统架构需求] [3-高性能.高并发.高可用的三高商城系统项目介绍] [4-Linux云 ...
- Java面试指北!13个认证授权常见面试题/知识点总结!| JavaGuide
大家好,我是 Guide哥!端午已过,又要开始工作学习啦! 我发现有很多小伙伴对认证授权方面的知识不是特别了解,搞不清 Session 认证.JWT 以及 Cookie 这些概念. 所以,根据我根据日 ...
- 《Spring Boot 实战派》--10.集成安全框架,实现安全 认证和授权
第10章 集成安全框架,实现安全 认证和授权 本章首先介绍如何使用Spring Security创建独立验证的管理员权限系统.会员系统,讲解如 何进行分表.分权限' 分登录入口.分认证接口.多注册接口 ...
- java 认证和授权_SpringSecurity一:认证和授权
什么是认证? 认证就是取得合法身份,比如京东需要用户登录以后才能才能下订单,这里的登录就是认证.登录成功以后就具有了合法身份可以继续进行下一步的操作. 常用的认证方式 常用的认证方式有基于sessio ...
最新文章
- 一致代价搜索_谷歌工程师发布最新技术ScaNN:可实现高效的向量相似性搜索
- LVM逻辑卷 (概述lvm,管理卷组 物理卷 逻辑卷,磁盘配额)
- JVM003_属性表
- 【MySql】MySql存储,游标,循环的简单使用
- python中stacked_python – Django管理员StackedInline定制
- android leaks工具,Androidx中ImmLeaksCleaner已经解决了InputMethodManager... - 简书
- 安卓10源码开发定制(21)GPS定位研究(3)修改GPS定位数据测试gps定位代码
- iOS 元素定位方式总结
- day01语法、变量、标识符、强制转换笔记
- Excel冻结首行/首列
- SAP 订单结算方式
- 给大家推荐一本书:你只是看起来很努力
- 【知识笔记】Debugging
- 依图科技创始人:如何对AI技术和产业发展更全面的理解?
- 两张图看清英伟达RTX 20系列显卡的新变化
- Python编程之四书五经
- vb.net mysql 查询,mysql-vb.net查询以显示数据表的特定行[基本]
- js数组计算假设一张纸为0.01米 对折多少次后能比珠穆朗玛峰高
- D15-Acwing-3.2-4.1代码实现
- 风向标瑞福进取昨发疯,真的转势了
热门文章
- 好用的 HTTP模块SuperAgent
- 牛奶布丁,我也会做了-o-
- 初级php工程师掌握技能,php工程师必须掌握的职业技能(上)
- 【NOWCODER】- Python:输入输出
- 二手车Task3 特征工程
- css圆角边框(css圆角边框无效)
- 连续5个涨停后,乐视网停牌核查
- Vue @click.stop 与 @mousewheel.prevent
- 实现串口通信数据帧打包与解析,串口通信可靠传输,屡试不爽的数据封包与状态机数据解析程序
- HDU 3974 Assign the task(树 并查集)