spring-authorization-server如何通过JWK Set Endpoint来获取公钥并验签的

参考文档:spring-authorization-server【版本1.2.2】

问题

在spring-authorization-server官方文档中提供了JWK Set Endpoint相关介绍,此端点主要返回JWK Set ,此JWK Set包含了授权服务提供的所有公钥集,具体可通过访问端点:/oauth2/jwks来获取。

但获取到公钥集如何来运用呢?如何来验签的呢?这是此次我需要了解的问题。

在了解基于private_key_jwt的客户端身份验证方法时,我了解到授权服务是如何通过客户端暴露的JWK Set Endpoint来获取到公钥集,然后通过拿到的公钥集又是如何验签的,解决了我的疑问。

代码梳理

通过梳理源码,经层层调用,我定位到几个主要的方法。
首先是JwtClientAssertionDecoderFactory#createDecoder(registeredClient)的调用:

...
private final Map<String, JwtDecoder> jwtDecoders = new ConcurrentHashMap<>();@Override
public JwtDecoder createDecoder(RegisteredClient registeredClient) {Assert.notNull(registeredClient, "registeredClient cannot be null");return this.jwtDecoders.computeIfAbsent(registeredClient.getId(), (key) -> {NimbusJwtDecoder jwtDecoder = buildDecoder(registeredClient);// 设置相关的校验逻辑参考JwtClientAssertionDecoderFactory#defaultJwtValidatorFactory方法 jwtDecoder.setJwtValidator(this.jwtValidatorFactory.apply(registeredClient));return jwtDecoder;});
}

此方法用来往jwtDecoders对象中维护NimbusJwtDecoder,相同的客户端ID存在则返回NimbusJwtDecoder对象,不存在则创建。

再说NimbusJwtDecoder的创建,在创建NimbusJwtDecoder时会执行NimbusJwtDecoder#processor()方法的调用,为jwkSource(此处的jwkSource为RemoteJWKSet对象)对象提供了远程调用JWK Set Endpoint的地址和方法。这样就有了可以获取JWK Set的条件了。

JWTProcessor<SecurityContext> processor() {// 通过jwkSetRetriever来远程调用获取JWK SetResourceRetriever jwkSetRetriever = new RestOperationsResourceRetriever(this.restOperations);// jwkSetUri为客户端暴露的JWK Set端点地址String jwkSetUri = this.jwkSetUri.apply(this.restOperations);// 此处获取到的jwkSource为RemoteJWKSetJWKSource<SecurityContext> jwkSource = jwkSource(jwkSetRetriever, jwkSetUri);ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();// 创建JWSVerificationKeySelector对象并给属性jwkSource、jwsAlgs初始化值,jwsAlgs包含一个对象JWSAlgorithm("RS256", Requirement.RECOMMENDED)jwtProcessor.setJWSKeySelector(jwsKeySelector(jwkSource));// Spring Security validates the claim set independent from NimbusjwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {});this.jwtProcessorCustomizer.accept(jwtProcessor);return jwtProcessor;
}

在这里插入图片描述

然后通过调用RemoteJWKSet#updateJWKSetFromURL方法来获取jwkSet。此处的jwkSet对象对应到指定算法的key集合,例如如果Key Type对应“RSA”,那么返回的jwkSet对象为RSAKey对象集。

private JWKSet updateJWKSetFromURL()throws RemoteKeySourceException {Resource res;try {res = jwkSetRetriever.retrieveResource(jwkSetURL);} catch (IOException e) {throw new RemoteKeySourceException("Couldn't retrieve remote JWK set: " + e.getMessage(), e);}JWKSet jwkSet;try {jwkSet = JWKSet.parse(res.getContent());} catch (java.text.ParseException e) {throw new RemoteKeySourceException("Couldn't parse remote JWK set: " + e.getMessage(), e);}jwkSetCache.put(jwkSet);return jwkSet;
}

在这里插入图片描述
然后再把jwkSet中所有的JWK对象的publicKey获取到,通过SignedJWT#verify来验签。

...
// 获取所有的publicKey集
List<? extends Key> keyCandidates = selectKeys(signedJWT.getHeader(), claimsSet, context);if (keyCandidates == null || keyCandidates.isEmpty()) {throw new BadJOSEException("Signed JWT rejected: Another algorithm expected, or no matching key(s) found");
}ListIterator<? extends Key> it = keyCandidates.listIterator();while (it.hasNext()) {// 根据算法返回JWSVerifier对象,例如“RS256”返回RSASSAVerifier对象JWSVerifier verifier = getJWSVerifierFactory().createJWSVerifier(signedJWT.getHeader(), it.next());if (verifier == null) {continue;}// 根据算法获取Signature对象,例如“RS256”对应 Signature.getInstance("SHA256withRSA"),然后对verifier设置publicKey,最后进行验签final boolean validSignature = signedJWT.verify(verifier);if (validSignature) {return verifyClaims(claimsSet, context);}if (! it.hasNext()) {// No more keys to try outthrow new BadJWSException("Signed JWT rejected: Invalid signature");}
}
...

上面是跟踪代码摘取的部分代码,相对比较乱,其实如果针对上面的调用把主要的代码逻辑摘取出来就可以构建自己的获取JWK Set和验签的逻辑。

自定义方法

远程调用获取JWK Set

/*** 根据JWK Set Endpoint地址获取JWK集合* * @param jwksUrl JWK Set Endpoint地址* @return JWK Set*/
public static JWKSet loadJWKSByUrl(String jwksUrl) throws IOException, ParseException {URL jwkSetURL = new URL(jwksUrl);ResourceRetriever jwkSetRetriever = new DefaultResourceRetriever(RemoteJWKSet.resolveDefaultHTTPConnectTimeout(),RemoteJWKSet.resolveDefaultHTTPReadTimeout(),RemoteJWKSet.resolveDefaultHTTPSizeLimit());Resource res = jwkSetRetriever.retrieveResource(jwkSetURL);JWKSet jwkSet = JWKSet.parse(res.getContent());return jwkSet;}

验签

/*** 根据公钥验证JWT(JSON WEB TOKEN)** @param jwksUrl JWK Set Endpoint地址* @param jwt     要验证的JWT(JSON WEB TOKEN)* @return 验证结果,true正确,false不正确*/public static boolean verifyJWT(String jwksUrl, String jwt) throws ParseException, IOException, JOSEException {Base64URL[] parts = JOSEObject.split(jwt);SignedJWT signedJWT = new SignedJWT(parts[0], parts[1], parts[2]);JWSHeader jwsHeader = JWSHeader.parse(parts[0].decodeToString());JWKMatcher jwkMatcher = JWKMatcher.forJWSHeader(jwsHeader);JWKSelector jwkSelector = new JWKSelector(jwkMatcher);List<JWK> jwkMatches = jwkSelector.select(loadJWKSByUrl(jwksUrl));// 获取公钥List<Key> sanitizedKeyList = new LinkedList<>();for (Key key : KeyConverter.toJavaKeys(jwkMatches)) {if (key instanceof PublicKey || key instanceof SecretKey) {sanitizedKeyList.add(key);}}ListIterator<? extends Key> it = sanitizedKeyList.listIterator();JWSVerifierFactory jwsVerifierFactory = new DefaultJWSVerifierFactory();while (it.hasNext()) {JWSVerifier verifier = jwsVerifierFactory.createJWSVerifier(jwsHeader, it.next());if (verifier == null) {continue;}final boolean validSignature = signedJWT.verify(verifier);return validSignature;}return false;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/744491.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

纯血鸿蒙来画龙!基于HarmonyOS ArkTS来操作SVG图片

大家好&#xff0c;龙年报喜&#xff0c;大地回春&#xff0c;作为程序员&#xff0c;以代码之名&#xff0c;表达对于龙年的祝福。本节将演示如何在基于HarmonyOS ArkTS的Image组件来实现画一条中国龙&#xff0c;祝大家“码”上“鸿”福到&#xff01; 创建应用 选择空模板…

FreeRTOS 的任务创建和删除

任务创建是我们第一个要学习的 API 函数&#xff0c;同时它也是 FreeRTOS 众多 API 函数中最复杂的一个&#xff0c;但是没办法&#xff0c;这个函数是我们第一个要学习的&#xff0c;也是非常重要的。 那么来看一下咱们本节的主要内容有哪些&#xff1a; 首先我们来介绍一下…

4款实用性前端动画特效分享(附在线演示)

分享4款非常不错的项目动画特效 其中有jQuery特效、canvas特效、CSS动画等等 下方效果图可能不是特别的生动 那么你可以点击在线预览进行查看相应的动画特效 同时也是可以下载该资源的 全屏图片视差旋转切换特效 基于anime.js制作全屏响应式的图片元素布局&#xff0c;通过左…

Linux系统部署Swagger Editor结合内网穿透实现公网管理本地接口文档

文章目录 Swagger Editor本地接口文档公网远程访问1. 部署Swagger Editor2. Linux安装Cpolar3. 配置Swagger Editor公网地址4. 远程访问Swagger Editor5. 固定Swagger Editor公网地址 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的 人工智能学习网站&#xf…

一种基于宏和serde_json实现的rust web中统一返回类

本人rust萌新&#xff0c;写web碰到了这个&#xff0c;基于ChatGPT和文心一言学了宏&#xff0c;强行把这玩意实现出来了&#xff0c;做个学习记录&#xff0c;如果有更好的方法&#xff0c;勿喷。 先看效果&#xff0c;注意不支持嵌套&#xff0c;且kv映射要用>(因为它这个…

Hadoop大数据应用:Yarn 节点实现扩容与缩容

目录 一、实验 1.环境 2.Yarn 节点扩容 3.Yarn 节点缩容 二、问题 1.yarn启动服务报错 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 主机架构软件版本IP备注hadoop NameNode &#xff08;已部署&#xff09; SecondaryNameNode &#xff08;已部署&…

力扣● 392.判断子序列 ● 115.不同的子序列

● 392.判断子序列 可以直接使用双指针的方法&#xff0c;2个指针分别从s、t开头出发&#xff0c;时间复杂度为O(t.size())。 但是这里用动规来做。Carl&#xff1a;掌握本题的动态规划解法是对后面要讲解的编辑距离的题目打下基础。 so绕一下&#xff0c;用昨天的● 1143.最…

直排开料机:木工行业的效率革命者

在木工行业中&#xff0c;开料机作为关键的生产设备&#xff0c;其选择直接关系到生产效率和产品质量。近年来&#xff0c;直排开料机以其独特的优势逐渐崭露头角&#xff0c;成为了众多企业的首选。那么&#xff0c;直排开料机究竟有哪些令人瞩目的优势呢&#xff1f; 一、高…

CrossOver24软件免费电脑虚拟机,快速在Mac和Linux上运行Windows软件

当然&#xff0c;除了之前提到的核心技术、兼容性和性能优化外&#xff0c;CrossOver2024还具有其他一些值得关注的性能特点&#xff1a; CrossOver Mac-安装包下载如下&#xff1a;https://wm.makeding.com/iclk/?zoneid50028 CrossOver linux-安装包下载如下&#xff1a;ht…

Spring Cloud Alibab 入门搭建,包含Nacos中心,注册服务发现服务,Feign请求,GateWay网关,sentinel限流

一、安装Nacos注册中心 1.1查看Nacos官网&#xff0c;安装Nacos服务&#xff0c;下载源码或者安装包 1.2启动服务&#xff0c;默认端口为8848&#xff0c; 二、创建服务注册&发现 2.1使用脚手架&#xff0c;创建注册服务和发现服务项目&#xff0c;我用的版本是2.6.13&…

mavros话题订阅后无法触发回调

前提 使用树莓派与pixhawk通信&#xff0c;安装好mavros&#xff0c;树莓派与pixhawk串口连接 启动节点mavros节点后&#xff0c;通过ros2 topic list可以查看到一系列话题 查看话题的类型可以去wiki mavros中查看 或者使用ros2 topic info 话题名称可以查看到 问题描述 订阅…

没有硬件基础可以学单片机吗?

没有硬件基础可以学单片机吗&#xff1f; 在开始前我分享下我的经历&#xff0c;我刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;一年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些 电气工程师学习方法和资料&#xff0c;让我不断提升自己&#xff0c…

一.算法基础

目录 1.算法基础 2.算法概念 3.时间复杂度--用来评估算法运行效率的一个式子 如何简单快速的判断算法复杂度? 4.空间复杂度 1.算法基础 2.算法概念 --静态动态 3.时间复杂度--用来评估算法运行效率的一个式子 ----一个单位!!! 1-在什么配置下运行(机器) 2-问题的规模…

①【Docker】Linux安装Docker容器教程

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ ①【Docker】Linux安装Docker容器教程 &#x…

slf4j 打印当前类和方法

spring initializr 自动包含依赖,也可以在 pom.xml 文件中添加 slf4j 的依赖,指定版本 例如我这边默认版本是 1.7.36 通过添加依赖修改版本为 1.7.32<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version…

JavaScript数组sort自定义排序

背景 刷LeetCode时&#xff0c;遇到一道简单的数组排序题&#xff1a; 问题 想着直接用js的数组sort自定义排序即可&#xff0c;奈何测试用例运行总是不通过&#xff0c;返回的一直都是原数组。 代码排查 复制代码到Firefox浏览器控制台运行&#xff0c;结果输出的是正确结果&a…

Nginx的日志怎么看,在哪看,access.log日志内容详解

Nginx 的日志文件通常位于服务器的文件系统中&#xff0c;具体位置可能因配置而异。以下是查看 Nginx 日志的几种方法&#xff1a; 1、查看访问日志&#xff1a;在默认配置下&#xff0c;Nginx 的访问日志文件路径为 /var/log/nginx/access.log。您可以通过命令 sudo cat /var…

3、设计模式之工厂模式1(Factory)

工厂模式是什么&#xff1f;     工厂模式是一种创建者模式&#xff0c;用于封装和管理对象的创建&#xff0c;屏蔽了大量的创建细节&#xff0c;根据抽象程度不同&#xff0c;主要分为简单工厂模式、工厂方法模式以及抽象工厂模式。 简单工厂模式 看一个具体的需求 看一个…

Spring Boot 集成 WebSocket 实例 | 前端持续打印远程日志文件更新内容(模拟 tail 命令)

这个是我在 CSDN 的第一百篇原则博文&#xff0c;留念&#x1f60e; #1 需求说明 先说下项目结构&#xff0c;后端基于 Spring Boot 3&#xff0c;前端为 node.js 开发的控制台程序。现在希望能够在前端模拟 tail 命令&#xff0c;持续输出后端的日志文件。 #2 技术方案 #2.…

蓝桥杯练习:景区导游

视频 UP主的博客 暴力做法&#xff0c;能过 42%数据。如果内存开 1e410 能过 40%&#xff0c;如果开 2e510就只能过 25% #include<bits/stdc.h> #define int long long #define endl \n const int N 1e410; using namespace std; //存两点的距离 typedef pair<in…