Cannot convert access token to JSON

授权服务颁发token(未进行公私钥加密)后,携带此token请求资源服务,提示此错误。

使用token可以在线解析,跟踪代码后问题出现JwtHelper类decodeAndVerify方法,内容如下:

...
public static Jwt decodeAndVerify(String token, SignatureVerifier verifier) {
Jwt jwt = decode(token);
jwt.verifySignature(verifier);
return jwt;
}
...

其中jwt.verifySignature(verifier)中verifier为空,在下一层JwtImpl类中,方法如下:

...
public void verifySignature(SignatureVerifier verifier) {
verifier.verify(this.signingInput(), this.crypto);
}
...

所以在资源服务中,必须对JwtAccessTokenConverter初始化setVerifier。

...
/**
* Description: 为签名验证和解析提供转换器<br>
* Details: 看起来 {@link org.springframework.security.jwt.crypto.sign.RsaVerifier} 已经被标记为过时了, 究其原因, 似乎 Spring 已经发布了一个新的产品 Spring Authorization Server, 有空再研究.
*
* @see <a href="https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Migration-Guide">OAuth 2.0 Migration Guide</a>
* @see <a href="https://spring.io/blog/2020/04/15/announcing-the-spring-authorization-server">Announcing the Spring Authorization Server</a>
* @see JwtAccessTokenConverter
*/
@SuppressWarnings("deprecation")
private JwtAccessTokenConverter jwtAccessTokenConverter() {
final JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setVerifier(new org.springframework.security.jwt.crypto.sign.RsaVerifier(retrievePublicKey()));
return jwtAccessTokenConverter;
} /**
* Description: 获取公钥 (Verifier Key)<br>
* Details: 启动时调用
*
* @return java.lang.String
* @author LiKe
* @date 2020-07-22 11:45:40
*/
private String retrievePublicKey() {
final ClassPathResource classPathResource = new ClassPathResource(AUTHORIZATION_SERVER_PUBLIC_KEY_FILENAME);
try (
// ~ 先从本地取读取名为 authorization-server.pub 的公钥文件, 获取公钥
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(classPathResource.getInputStream()))
) {
log.debug("{} :: 从本地获取公钥 ...", RESOURCE_ID);
return bufferedReader.lines().collect(Collectors.joining("\n"));
} catch (IOException e) {
// ~ 如果本地没有, 则尝试通过授权服务器的 /oauth/token_key 端点获取公钥
log.debug("{} :: 从本地获取公钥失败: {}, 尝试从授权服务器 /oauth/token_key 端点获取 ...", RESOURCE_ID, e.getMessage());
final RestTemplate restTemplate = new RestTemplate();
final String responseValue = restTemplate.getForObject(AUTHORIZATION_SERVER_TOKEN_KEY_ENDPOINT_URL, String.class); log.debug("{} :: 授权服务器返回原始公钥信息: {}", RESOURCE_ID, responseValue);
return JSON.parseObject(JSON.parseObject(responseValue).getString("data")).getString("value");
}
}
...

最终定位出现问题是因为没有正确初始化signingKeyverifierKey

先说说 verifierKey 和 verifier, 默认值是一个 6 位的随机字符串 (new RandomValueStringGenerator().generate()), 和它 “配套” 的 verifier 是持有这个 verifierKey 的 MacSigner (用 HMACSHA256 算法验证 Signature) / RsaVerifier (用 RSA 公钥验证 Signature), 在 JwtAccessTokenConverter 的 decode 方法中作为签名校验器被传入 JwtHelper 的 decodeAndVerify(@NotNull String token, org.springframework.security.jwt.crypto.sign.SignatureVerifier verifier)

下面例子为使用原始MacSigner HMACSHA256 算法例子:

...
private JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("***");
try {
converter.afterPropertiesSet();
} catch (Exception e) {
e.printStackTrace();
}
return converter;
}
...

最后问题完美解决。

而在 JwtHelper 的目标方法中, 首先把 token 的三个部分 (以 . 分隔的) 拆分出来, Base64.urlDecode 解码. 再用我们传入的 verifier 将 “Header.Payload” 编码 (如果是 RSA, 就是公钥.) 并与拆分出来的 Signature 部分比对 (Reference: org.springframework.security.jwt.crypto.sign.RsaVerifier#verify).

对应的, signer 和 signingKey 作为签名 “组件” 存在, (可以看到在默认情况下, JwtAccessTokenConverter 对 JWT 的 Signature 采用的是对称加密, signingKey 和 verifierKey 一致) 在 JwtHelper 的 encode(@NotNull CharSequence content, @NotNull org.springframework.security.jwt.crypto.sign.Signer signer) 方法中, 被用于将 “Header.Payload” 加密 (如果是 RSA, 就是私钥) (Reference: org.springframework.security.jwt.crypto.sign.RsaSigner#sign).

所以算法本质上不是对 JWT 整体进行加解密, 而是对其中的 Signature 部分

参考

JWT在线解析

Java JwtAccessTokenConverter.setVerifierKey方法代碼示例 - 純淨天空 (vimsky.com)

JwtAccessTokenConverter问题整理的更多相关文章

  1. dotNET跨平台相关文档整理

    一直在从事C#开发的相关技术工作,从C# 1.0一路用到现在的C# 6.0, 通常情况下被局限于Windows平台,Mono项目把我们C#程序带到了Windows之外的平台,在工作之余花了很多时间在M ...

  2. UWP学习目录整理

    UWP学习目录整理 0x00 可以忽略的废话 10月6号靠着半听半猜和文字直播的补充看完了微软的秋季新品发布会,信仰充值成功,对UWP的开发十分感兴趣,打算后面找时间学习一下.谁想到学习的欲望越来越强 ...

  3. SQL Server 常用内置函数(built-in)持续整理

    本文用于收集在运维中经常使用的系统内置函数,持续整理中 一,常用Metadata函数 1,查看数据库的ID和Name db_id(‘DB Name’),db_name('DB ID') 2,查看对象的 ...

  4. kafka学习笔记:知识点整理

    一.为什么需要消息系统 1.解耦: 允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束. 2.冗余: 消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险. ...

  5. JAVA程序员常用软件整理下载

    ********为了大家学习方便,特意整理软件下载如下:*************Java类软件:-------------------------------JDK7.0:http://pan.ba ...

  6. js数组学习整理

    原文地址:js数组学习整理 常用的js数组操作方法及原理 1.声明数组的方式 var colors = new Array();//空的数组 var colors = new Array(3); // ...

  7. GJM : C#设计模式汇总整理——导航 【原创】

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

  8. 整理下.net分布式系统架构的思路

    最近看到有部分招聘信息,要求应聘者说一下分布式系统架构的思路.今天早晨正好有些时间,我也把我们实际在.net方面网站架构的演化路线整理一下,只是我自己的一些想法,欢迎大家批评指正. 首先说明的是.ne ...

  9. 安卓GreenDao框架一些进阶用法整理

    大致分为以下几个方面: 一些查询指令整理 使用SQL语句进行特殊查询 检测表字段是否存在 数据库升级 数据库表字段赋初始值 一.查询指令整理 1.链式执行的指令 return mDaoSession. ...

随机推荐

  1. JAVA之垃圾收集器

    概述 垃 圾收集 Garbage Collection 通常被称为"GC",它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了. jvm 中,程序计 ...

  2. 二维数组与稀疏数组的转换---dataStructures

    首先我们看一个需求 在11 * 11 的五子棋的棋盘中 我们使用0代表十字交叉点也是无效的数据 用1代表黑棋 用2代表蓝棋 那么所看到的棋盘如下 改用数字显示后就如一下样式 现在我们需要将怎个棋盘存储 ...

  3. RocketMQ架构原理解析(一):整体架构

    RocketMQ架构原理解析(一):整体架构 RocketMQ架构原理解析(二):消息存储(CommitLog) RocketMQ架构原理解析(三):消息索引(ConsumeQueue & I ...

  4. HTML、CSS、Javascript、jQuery、Xml

    HTML HTML简介 Hyper Text Markup Language (超文本标记语言)简写:HTML.通过标签来标记要显示的网页中的各个部分.网页文件本身是一种文本文件,通过在文本文件中添加 ...

  5. 【源码阅读】VictoriaMetrics中理解vm-backup中设置origin地址的用途

    lib/backup/actions/backup.go: // 118 行 partsToCopy := common.PartsDifference(srcParts, dstParts) //要 ...

  6. Kubernetes集群PV和PVC详解

    Kubernetes集群高级存储资源PV及PVC 文章目录 Kubernetes集群高级存储资源PV及PVC 1.高级存储PV和PVC概念部分 2.PV和PVC资源的生命周期 3.PV资源介绍与案例配 ...

  7. maven 中的工程依赖和层级依赖?

    一.什么是工程依赖? 思考问题?1.1一旦开始分模块开发的时候,之前的所有包都会被拆分成一个一个的项目 model mapper service ... 其实mapper需要model的支持,怎么解决 ...

  8. Supervisor多进程管理 异常自动重启 可视化管理

    一.序言 Supervisor是多进程管理工具,在Docker中相关联的进程能够通过supervisor来管理. 微服务项目开发阶段,可用于微服务子项目的启动管理. 支持web可视化管理,能够极大方面 ...

  9. 计算机开放电子书 2021 RC2

    下载方式 pip install CDNDrive # 或 # pip install git+https://github.com/apachecn/CDNDrive cdrive download ...

  10. 实现表单input文本框不可编辑的三种方法

    感谢原文作者:青灯夜游 原文链接:https://www.php.cn/div-tutorial-413133.html 目录 问题 实现方式 1.οnfοcus=this.blur() 2.read ...