苹果https java_apple登录 后端java实现最终版-程序员宅基地

技术标签: 苹果https java  

import com.alibaba.fastjson.JSONArray;

import com.alibaba.fastjson.JSONObject;

import com.auth0.jwk.Jwk;

import com.helijia.appuser.modules.user.vo.AppleCredential;

import com.helijia.common.api.model.ApiException;

import com.helijia.user.util.HttpClientUtil;

import com.helijia.user.util.UserCode;

import io.jsonwebtoken.Claims;

import io.jsonwebtoken.ExpiredJwtException;

import io.jsonwebtoken.Jws;

import io.jsonwebtoken.JwtParser;

import io.jsonwebtoken.Jwts;

import org.apache.commons.lang3.StringUtils;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Service;

import org.apache.commons.codec.binary.Base64;

import java.security.PublicKey;

import java.util.Map;

/**

* Created by laohu May 18 2020

*/

@Service

public class AppleService {

private static final Logger LOGGER = LoggerFactory.getLogger(AppleService.class);

private static final String AUTH_URL = "/auth/keys";

@Value("${apple.login.auth.url}")

public String appleUrl;

public void verify(AppleCredential credential) {

try {

String[] identityTokens = credential.getIdentityToken().split("\\.");

Map firstDate = JSONObject

.parseObject(new String(Base64.decodeBase64(identityTokens[0]), "UTF-8"));

Map secondData = JSONObject

.parseObject(new String(Base64.decodeBase64(identityTokens[1]), "UTF-8"));

String kid = String.valueOf(firstDate.get("kid"));

String aud = String.valueOf(secondData.get("aud"));

String sub = String.valueOf(secondData.get("sub"));

// 1.验证apple官方 2.sub和openId的一致性

if (verify(credential.getIdentityToken(), kid, aud, sub)) {

if(StringUtils.isEmpty(credential.getOpenId())) {

credential.setOpenId(sub);

}

return;

}

} catch (Exception e) {

LOGGER.error("apple identityToken verify error", e);

}

throw new ApiException(UserCode.USER_THIRD_ACCESSTOKEN_FAIL.getApiCode(),

UserCode.USER_THIRD_ACCESSTOKEN_FAIL.getApiMessage());

}

/**

* 验证

*

* @param identityToken APP获取的identityToken

* @param aud

* @param sub

* @return true/false

*/

private boolean verify(String identityToken, String kid, String aud, String sub) {

PublicKey publicKey = getPublicKey(kid);

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

jwtParser.requireIssuer(appleUrl);

jwtParser.requireAudience(aud);

jwtParser.requireSubject(sub);

try {

Jws claim = jwtParser.parseClaimsJws(identityToken);

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

return true;

}

return false;

} catch (ExpiredJwtException e) {

LOGGER.error("apple identityToken expired", e);

return false;

} catch (Exception e) {

LOGGER.error("apple identityToken illegal", e);

return false;

}

}

/**

* @param kid 通过kid获取对应的PublicKey

* {

*    "keys": [

*      {

*        "kty": "RSA",

*        "kid": "86D88Kf",

*        "use": "sig",

*        "alg": "RS256",

*        "n": "iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ",

*        "e": "AQAB"

*      },

*      {

*        "kty": "RSA",

*        "kid": "eXaunmL",

*        "use": "sig",

*        "alg": "RS256",

*        "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw",

*        "e": "AQAB"

*      }

*    ]

*  }

* @return 构造好的公钥

*/

private PublicKey getPublicKey(String kid) {

try {

String str = HttpClientUtil.get(appleUrl + AUTH_URL);

JSONObject data = JSONObject.parseObject(str);

JSONArray jsonArray = data.getJSONArray("keys");

if(jsonArray.isEmpty()) {

return null;

}

for (Object object : jsonArray) {

JSONObject json = ((JSONObject)object);

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

json = ((JSONObject)object);

Jwk jwa = Jwk.fromValues(json);

return jwa.getPublicKey();

}

}

} catch (final Exception e) {

LOGGER.error("apple getPublicKey error", e);

}

return null;

}

}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_33216057/article/details/114254169

智能推荐

vue中input输入框限制输入小数点后1位_vue el-input 动态设置小数点后一位-程序员宅基地

文章浏览阅读822次。vue中input输入框限制输入小数点后1位:<input @input="InputChange" v-model="clllci" /> InputChange(e) { console.log(e.target.value.match(/^\d*(\.?\d{0,2})/g)[0],6666) this.clllci = e.target.value.match(/^\d*(\.?\d{0,2})/g)[0] || null; },..._vue el-input 动态设置小数点后一位

gopdf的基本使用_gopdf 换行-程序员宅基地

文章浏览阅读364次。pdf.Cell(nil, "早上好,文艺范")使用循环让文本自动换行。//绘制带圆角的矩形。_gopdf 换行

Android实现抖音无水印视频,最新大厂Android社招面试经验汇总-程序员宅基地

文章浏览阅读920次,点赞12次,收藏14次。最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2019-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。还有高级架构技术进阶脑图、Android开发面试专题资料帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

ubuntu 18.04 python3安装 版本更换 指定python版本运行脚本 anaconda3冲突_把/usr/bin/python和/usr/bin/python3链接到anaconda/bin-程序员宅基地

文章浏览阅读1.7k次。一、python3安装sudo apt install python3二、版本更换1查询已安装的python版本ls /usr/bin/python*显示在update-alternatives中有的python版本sudo update-alternatives --list python添加python版本到update-alternatives中,后面的数字越高优先级越高..._把/usr/bin/python和/usr/bin/python3链接到anaconda/bin

一起学ORBSLAM2(5)ORBSLAM的单目视觉处理方式_orbslam的单目尺度-程序员宅基地

文章浏览阅读6.4k次,点赞2次,收藏38次。单目相机由于深度是未知的,因此我们需要对其进行初始化,在ORB-SLAM中将其用单独的类来表示,并将它写成单独的文件initializer.cc,注意单目相机即使在进行初始化之后,仍然存在尺度问题,初始化将第一帧的位移作为单位长度,后面的深度和位移都是依据这一标准进行的,所以尺度问题是单目slam的理论缺点。单目slam初始化需要两帧进行,第一帧作为参考初始化帧,第二帧作为当前帧。在第一帧来临时建..._orbslam的单目尺度

IK-Analyzer的maven项目依赖_ikanalyzer依赖-程序员宅基地

文章浏览阅读3.5k次。Java中文分词包——IK-Analyzer的maven项目依赖 <dependency> <groupId>com.janeluo</groupId> <artifactId>ikanalyzer</artifactId> <version>2012_u6</version&..._ikanalyzer依赖

随便推点

详解Excel中最常用的查找公式Vlookup及Sumifs_sumifs和vlookup合起来设公式-程序员宅基地

文章浏览阅读4.2k次,点赞3次,收藏8次。详解excel数据查找分析定位中最常用的两个函数,vlookup以及sumifs_sumifs和vlookup合起来设公式

H3C的R390X G2服务器配置HCI时,关于磁盘配置jbod模式_华三服务器开启硬盘直通-程序员宅基地

文章浏览阅读5.9k次。前言 配置深信服超融合时,用到的是第三方服务器,一般情况下网络构架搭建好和设备齐全后,经常会遇到问题是:HCI平台配置虚拟存储时如果第三方服务器有RAID,建议配置为直通(JBOD)模式,如果RAID卡不支持该模式,则要求将每个硬盘做成RAID 0逻辑盘,同时建议关闭RAID卡的写缓存,此时不支持硬盘热拔插!(但是不同的第三方服务器的jbod配置不一样!)H3C的R390X G2..._华三服务器开启硬盘直通

VisualStudio2022 关于C4996和C6031错误的解决方法_vs2022 c4996-程序员宅基地

文章浏览阅读1.1k次,点赞6次,收藏9次。微软不建议再使用C的传统库函数scanf,strcpy,sprintf等,所以直接使用这些库函数会提示C4996错误。_vs2022 c4996

JSP基本使用_jsp 用法-程序员宅基地

文章浏览阅读256次。本文是个人看视频总结,适合初学者标记含义说明性的标记 通常会放在文件的顶部包含普通的Java代码,_jspService方法外部包含普通的Java代码, _jspService方法内部包含普通的Java代码,通常是用来赋值和展示jsp文件中注释内容JSP编译:设计一个登录页面查询展示金额的例子部分代码:文件名称:showBal_jsp 用法

Glide介绍及基本使用方法_glide使用-程序员宅基地

文章浏览阅读4k次,点赞6次,收藏34次。本文为Glide的基本使用技巧,基于Glide官方文档编写,适合未接触Glide的开发人员参考。_glide使用

c++ ifstream 读文件 最后一行读两次-程序员宅基地

文章浏览阅读908次。http://blog.csdn.net/rebel_321/article/details/4927464 用ifstream的eof(),竟然读到文件最后了,判断eof还为false。网上查找资料后,终于解决这个问题。参照文件:http://tuhao.blogbus.com/logs/21306687.html 在使用C/C++读文件的时候,一定都使用过eof()这个..._ifstream 读到了文件末尾以外的数据

推荐文章

热门文章

相关标签