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. 常用Cron表达式范例

    描述 表达式 每隔5秒执行一次 */5 * * * * ? 每隔1分钟执行一次 0 */1 * * * ? 每天23点执行一次 0 0 23 * * ? 每天凌晨1点执行一次 0 0 1 * * ? ...

  2. 【解决了一个小问题】vmselect对应的vmstorage端口配置错误导致的问题

    从vmselect查询的时候,出现如下错误: error when executing query="up" on the time range (start=1639388706 ...

  3. unity3d之public变量引发错误

    public变量引发错误 在vs ide中怎么更改也无效 后来发现public里面的值一直不改变,手动改之.

  4. 网络流 HLPP 板子

    #include<bits/stdc++.h> using namespace std; const int MM=4e5+5,inf=0x3f3f3f3f; int n,m,s,t,to ...

  5. Qt之QFontDialog

    widget.h: #ifndef WIDGET_H #define WIDGET_H #include <QWidget> class Widget : public QWidget { ...

  6. java匿名内部类-应用

    1 package face_09; 2 3 import sun.jvm.hotspot.ui.action.ShowAction; 4 5 interface Inter{ 6 void show ...

  7. 多线程-停止线程方式-Interrupt

    1 package multithread4; 2 /* 3 * 停止线程: 4 * 1,stop方法. 5 * 6 * 2,run方法结束. 7 * 8 * 怎么控制线程的任务结束呢? 9 * 任务 ...

  8. Nginx全面介绍 什么是Nginx?

    目录 一:Nginx全面讲解 1.简介: 2.nginx的用武之地 3.关于代理(解析含义作用) 二:正向代理 三:反向代理 四:项目应用场景 五:正向代理与反向代理区别 1.正向代理 2.反向代理 ...

  9. json模块 os模块 文件加密

    目录 一:random随机模块 二:os模块 三:文件处理选择任意视频 四:sys模块 五:实现文件执行加密操作 六:json 序列化模块 七:json序列化 反序列化 八:json 文件写读方式 九 ...

  10. 学习JAVAWEB第十四天

    ## JSP:入门学习 1. 概念: * Java Server Pages: java服务器端页面 * 可以理解为:一个特殊的页面,其中既可以指定定义html标签,又可以定义java代码 * 用于简 ...