ios 登录 java 后台,IOS苹果登录sign in with apple后端校验

汲永嘉
2023-12-01

IOS苹果登录sign in with apple后端校验

最近新开发的app在IOS平台app store connent提审的时候,被拒了,原因是app上如果有接第三方登陆(比如微信,微博,facebook等),那就必须要接apple id登陆,坑爹~苹果霸权啊!然而没办法,所以只能接入苹果登录。

APP端的接入可以看上一篇博客:iOS苹果授权登录(Sign in with Apple)/Apple登录/苹果登录集成教程,下面我来说一下对接苹果登陆的后端验证模块。

这里先说一下apple id登陆的主要流程和涉及到的一些知识点。首先apple登陆的时序图如下:

先是app和苹果服务器通信获得identitytoken,然后把identitytoken交给业务后台验证,验证通过就可以了。其中appServer涉及到的验证,就是identitytoken,其实identitytoken就是一个jws,至于校验jws,其实是有现成的jar包可以实现,验证jws的签名,保证数据没有被篡改之后,还要校验从identitytokendecode出来的nonce,iss,aud,exp,主要是iss和exp这两个。下面我直接上代码:

一、苹果登录JAVA后台校验:JWT的identityToken验证模式

话不多说直接上代码:

1、app端请求appleServer返回的identityToken

"identityToken":"ZXlKcmFXUWlPaUpsV0dGMWJtMU1JaXdpUndje***xMXpZZ3BiYWRIWHdGVEtR4ejRPZTBhUkdtcHZOZFpWVkJGQjN4OU13"

就是一个JWT的token

2、有2次验证,分别调取apple提供的接口即可

private final String APPLE_AUTH_URL = "https://appleid.apple.com/auth/keys";

private final String ISS = "https://appleid.apple.com";

一次是利用token获取解密的publicKey;另一次就是再校验这个publicKey。

代码如下:

public boolean verify(AppleLoginVO appleLoginVO) {

//这里传过来的identityToken应该是三个.分割,解密之后

String identityToken = appleLoginVO.getIdentityToken();

try {

if (identityToken.split("\\.").length 1){

String firstDate = new String( Base64.decodeBase64(identityToken.split("\\.")[0]),"UTF-8");

String claim = new String(Base64.decodeBase64(identityToken.split("\\.")[1]), "UTF-8");

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 = getPublicKey(kid);

if (publicKey == null) {

log.error("Apple have no info data!");

return false;

}

boolean reuslt = verify(publicKey, identityToken, aud, sub);

if (reuslt) {

log.info("苹果登录授权成功");

return true;

}

}

} catch (Exception e) {

log.error("苹果登录授权异常:", LogUtil.getStack(e));

e.printStackTrace();

}

log.error("identityToken格式不正确");

return false;

}

private PublicKey getPublicKey(String kid) {

try {

JSONObject debugInfo = getHttp(APPLE_AUTH_URL);

if (debugInfo == null) {

return null;

}

JSONObject jsonObject = debugInfo.getJSONObject("body");

String keys = jsonObject.getString("keys");

JSONArray jsonArray = JSONObject.parseArray(keys);

if (jsonArray.isEmpty()) {

return null;

}

for (Object object : jsonArray) {

JSONObject json = ((JSONObject) object);

if (json.getString("kid").equals(kid)) {

String n = json.getString("n");

String e = json.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 e) {

log.error("getPublicKey异常!", LogUtil.getStack(e));

e.printStackTrace();

}

return null;

}

private boolean verify(PublicKey key, String jwt, String audience, String subject){

boolean result = false;

JwtParser jwtParser = Jwts.parser().setSigningKey(key);

jwtParser.requireIssuer(ISS);

jwtParser.requireAudience(audience);

jwtParser.requireSubject(subject);

try {

JwsClaims claim = jwtParser.parseClaimsJws(jwt);

if (claim != null claim.getBody().containsKey("auth_time")) {

return true;

}

} catch (ExpiredJwtException e) {

log.error("getPublicKey异常{苹果identityToken过期}", LogUtil.getStack(e));

} catch (SignatureException e) {

log.error("getPublicKey异常{苹果identityToken非法}", LogUtil.getStack(e));

}

return result;

}

引入mavon:

dependency

groupIdio.jsonwebtoken/groupId

artifactIdjjwt/artifactId

version0.9.0/version

/dependency

3、封装的httpclient请求:

private JSONObject getHttp(String url) {

log.info("[请求地址]: " + url);

JSONObject resultJson = new JSONObject();

resultJson.put("code", -1);

CloseableHttpClient httpclient = HttpClients.createDefault();

try {

HttpGet httpPost = new HttpGet(url);

CloseableHttpResponse response = httpclient.execute(httpPost);

try {

HttpEntity entity = response.getEntity();

if (response.getStatusLine().getStatusCode() == 200) {

String resp = EntityUtils.toString(entity);

resultJson.put("code", 0);

resultJson.put("body", JSONObject.parseObject(resp));

} else {

resultJson.put("code", response.getStatusLine().getStatusCode());

log.error("[错误码] :" + response.getStatusLine().getStatusCode());

log.error("[请求地址] :" + url);

}

} finally {

response.close();

}

} catch (ClientProtocolException e) {

log.error("[异常] :", LogUtil.getStack(e));

} catch (IOException e) {

log.error("[异常] :", LogUtil.getStack(e));

} finally {

try {

httpclient.close();

} catch (IOException e) {

log.error("[httpclient 关闭异常] : ", LogUtil.getStack(e));

}

}

return resultJson;

}

IOS苹果登录sign in with apple后端校验 相关文章

axios网络请求

人的一生就像一篇文章,只有经过多次精心修改,才能不断完善。 你好,我是梦阳辰,期待与你相遇! 文章目录 01.概述 02.axios请求方式 使用vue-axios(重点) axios模块封装(重点) 03.axios拦截器 01.概述 Axios 是一个基于 promise 的 HTTP 库,可以用在浏

uniapp保持自动登入

在应用中保持登录状态是一个应用常见的需求,本文简单介绍下在 uni-app 中如何保存用户登录状态。 简介 uni-app 中不支持读写 cookie,所以不能如传统的应用那样通过读取 cookie 来判断是否是登录状态。 在 uni-app 进行登录操作时,可以将需要校验的数据放

Unity网络编程四:客户端与服务端进行数据传输(Unity登录系统的实现)

前言: 我们在完成服务端的搭建与客户端连接客户端的功能后,需要实现两者之间的通信功能 本次制作Unity登录界面来学习使用Unity服务端与客户端进行数据传输 客户端: UI搭建 首先就是添加两个输入框,在Hierarchy面板添加两个输入框Input Field,分别命名为U

Spring Security OAuth2.0认证授权六:前后端分离下的登录授权

历史文章 Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二:搭建资源服务 Spring Security OAuth2.0认证授权三:使用JWT令牌 Spring Security OAuth2.0认证授权四:分布式系统认证授权 Spring Security OAuth2.

登录和注册,要自己建user表,注册进来的密码要加密加盐1

#select * from user where username="%s";' % username #登陆 import hashlib import string, datetime import pymysql # 检查用户是否在数据库中 def check_user_exist(username): sql = "select pw_md5 from %s where uname = '%s'" % (table_name, usern

登录和注册,要自己建user表,注册进来的密码要加密加盐

#select * from user where username="%s";' % username #注册 import hashlib import string import pymysql # 检查表是否存在,不存在先建表 def checktable_isexist(): cur.execute("show tables like '%s'" % table_name) if not cur.fetchall(): create

Linux expect 远程自动登录机器并执行命令

0 运行环境 两台虚拟机:Ubuntu 18 x64 Xshell 6 1 expect 介绍 expect 是一个自动化交互套件,主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信。 注意 expect 脚本能够执行的前提是本地机器安装了 expect 1.1 expect 安装 安

微信小程序登录

view view class="container" view class="btn" button open-type="getUserInfo" @getuserinfo="getUserInfo"/button 微信一键登录 /view /view JavaScript getUserInfo() { uni.login({ provider:'weixin', success(logRes) { console.log(logRes) uni.getU

[Abp vNext微服务实践] - vue-element-admin登录二

简介:# Vue Element Admin是基于vue、element ui开发的后台管理ui,abp vNext是abp新一代微服务框架。本篇将会介绍如何改造Vue Element Admin权限验证并接入abp vNext的微服务权限模块。上篇已经介绍过Vue Element Admin登录过程,并实现了假登录,本篇将介

[Abp vNext微服务实践] - vue-element-admin登录一

简介# 之前的技术路线本来是angular的,后来经过一段时间的开发还是打算选择vue,原因是vue简单丰富,尽管angular规范强大,但是组件库都不太符合国人风格。看到GitHub上Vue Element Admin这么高的人气后就毫不犹豫的选择了这个后台管理框架。作为一个后端开

 类似资料: