详细分析Spring Security OAuth2中的JwtAccessTokenConverter基本知识(附Demo)

news2024/10/7 10:19:58

目录

  • 前言
  • 1. 基本知识
  • 2. Demo
  • 3. 实战

前言

  1. java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
  2. 【Java项目】实战CRUD的功能整理(持续更新)

1. 基本知识

JwtAccessTokenConverter 是 Spring Security OAuth2 中的一个重要组件,主要用于生成和验证 JSON Web Token (JWT)

  1. JWT 的结构
    JWT 由三部分组成:头部(Header)、负载(Payload)和签名(Signature)
    头部通常包含令牌的类型(JWT)和签名算法(如 HMAC SHA256)
    负载部分包含声明(Claims),即与用户相关的信息,例如权限、过期时间等
    签名是使用头部和负载生成的,用于验证令牌的完整性

  2. 类继承关系
    JwtAccessTokenConverter 继承自 AbstractTokenConverter,并实现了 AccessTokenConverter 接口
    该类负责将 OAuth2 访问令牌与 JWT 进行转换和处理

  3. 主要方法
    encode:用于生成 JWT,接受 OAuth2AccessToken 和 OAuth2Authentication 参数
    decode:用于解析和验证 JWT,返回 OAuth2AccessToken 实例

常用的几个类如下:

  • Access Token
    OAuth2 中的访问令牌,用于授权客户端访问受保护资源
    JWT 是一种常用的访问令牌格式

  • OAuth2Authentication
    封装与用户相关的身份验证信息,包括用户的身份和权限

  • Additional Information
    JWT 中可以包含附加信息,通常用于存储用户 ID、用户名、权限等自定义字段

2. Demo

  1. 签名密钥:在 main 方法中使用 demo.setSigningKey(signingKey); 设置签名密钥,以确保生成的 JWT 是安全的

  2. 用户权限和身份验证:使用 SimpleGrantedAuthority 创建用户的角色,并通过 TestingAuthenticationToken 实现身份验证

  3. OAuth2AccessToken:创建访问令牌,附加用户 ID、用户名和权限信息

  4. JWT 生成与解码:调用 encode 方法生成 JWT,然后使用 Base64 解码打印出 JWT 的头部和负载部分

常用的xml配置文件如下:

<!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.5.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-jwt</artifactId>
    <version>1.1.0.RELEASE</version> <!-- 确保版本与 Spring Security 兼容 -->
</dependency>

执行代码如下:

package JwtDemo;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import java.util.*;

public class JwtDemo extends JwtAccessTokenConverter {

    public static void main(String[] args) {
        JwtDemo demo = new JwtDemo();
        String signingKey = "your-signing-key"; // 请替换为你的签名密钥
        demo.setSigningKey(signingKey);

        // 创建用户权限
        Set<SimpleGrantedAuthority> authorities = new HashSet<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));

        // 创建 OAuth2Request
        OAuth2Request request = new OAuth2Request(null, "client_id",
                authorities, true, null, null, null, null, null);

        // 创建 Authentication
        Authentication authentication = new TestingAuthenticationToken("user", "password", authorities);

        // 创建 OAuth2Authentication
        OAuth2Authentication oauth2Authentication = new OAuth2Authentication(request, authentication);

        // 创建 OAuth2AccessToken 实例
        DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken("your-jwt-token");
        Map<String, Object> additionalInformation = new HashMap<>();
        additionalInformation.put("user_id", "12345"); // 添加用户信息
        additionalInformation.put("user_name", "user"); // 添加用户名
        additionalInformation.put("authorities", authorities); // 添加权限
        token.setAdditionalInformation(additionalInformation);

        // 使用 JwtDemo 生成 JWT
        String jwt = demo.encode(token, oauth2Authentication);
        System.out.println("生成的 JWT: " + jwt);

        String[] chunks = jwt.split("\\.");
        Base64.Decoder decoder = Base64.getUrlDecoder();

        try {
            String header = new String(decoder.decode(chunks[0]));
            String payload = new String(decoder.decode(chunks[1]));
            System.out.println("Header: " + header);
            System.out.println("Payload: " + payload);
        } catch (IllegalArgumentException e) {
            System.err.println("解码时出错: " + e.getMessage());
        }
    }
}

// 自定义的 Authentication 实现
class TestingAuthenticationToken extends org.springframework.security.authentication.UsernamePasswordAuthenticationToken {
    public TestingAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        super(principal, credentials, authorities);
    }
}

截图如下:

在这里插入图片描述

3. 实战

已Bledex源码实战为例

@Configuration  // 标记为配置类,Spring会自动扫描并加载
@ConditionalOnProperty(prefix = "blade.security.oauth2", name = "storeType", havingValue = "jwt", matchIfMissing = true) 
// 当配置中 "blade.security.oauth2.storeType" 为 "jwt" 时,才加载此配置类
public class JwtTokenStoreConfiguration {

	/**
	 * 使用 jwtTokenStore 存储 token
	 */
	@Bean  // 将方法的返回值注册为 Spring 的 Bean
	public TokenStore jwtTokenStore(JwtProperties jwtProperties) {
		// 创建 JwtTokenStore 实例,并传入 JwtAccessTokenConverter
		return new JwtTokenStore(jwtAccessTokenConverter(jwtProperties));
	}

	/**
	 * 用于生成 jwt
	 */
	@Bean  // 将方法的返回值注册为 Spring 的 Bean
	public JwtAccessTokenConverter jwtAccessTokenConverter(JwtProperties jwtProperties) {
		JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
		// 设置 JWT 签名密钥
		accessTokenConverter.setSigningKey(jwtProperties.getSignKey());
		return accessTokenConverter;  // 返回配置好的 JwtAccessTokenConverter
	}

	/**
	 * 用于扩展 jwt
	 */
	@Bean
	@ConditionalOnMissingBean(name = "jwtTokenEnhancer") 
	// 当 Spring 容器中没有名为 "jwtTokenEnhancer" 的 Bean 时,才加载此 Bean
	public TokenEnhancer jwtTokenEnhancer(JwtAccessTokenConverter jwtAccessTokenConverter, JwtProperties jwtProperties) {
		// 创建并返回自定义的 TokenEnhancer
		return new BladeJwtTokenEnhancer(jwtAccessTokenConverter, jwtProperties);
	}

}

对应的自定义类如下:

@AllArgsConstructor  // 自动生成一个包含所有字段的构造函数
public class BladeJwtTokenEnhancer implements TokenEnhancer {

	private final JwtAccessTokenConverter jwtAccessTokenConverter;  // JWT 访问令牌转换器
	private final JwtProperties jwtProperties;  // JWT 配置属性

	@Override
	public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
		// 从身份验证对象中获取用户的详细信息
		BladeUserDetails principal = (BladeUserDetails) authentication.getUserAuthentication().getPrincipal();

		// token 参数增强
		Map<String, Object> info = new HashMap<>(16);  // 创建一个新的 HashMap 用于存储附加信息
		info.put(TokenUtil.CLIENT_ID, TokenUtil.getClientIdFromHeader());  // 获取客户端 ID
		info.put(TokenUtil.USER_ID, Func.toStr(principal.getUserId()));  // 获取用户 ID
		info.put(TokenUtil.DEPT_ID, Func.toStr(principal.getDeptId()));  // 获取部门 ID
		info.put(TokenUtil.POST_ID, Func.toStr(principal.getPostId()));  // 获取职位 ID
		info.put(TokenUtil.ROLE_ID, Func.toStr(principal.getRoleId()));  // 获取角色 ID
		info.put(TokenUtil.TENANT_ID, principal.getTenantId());  // 获取租户 ID
		info.put(TokenUtil.OAUTH_ID, principal.getOauthId());  // 获取 OAuth ID
		info.put(TokenUtil.ACCOUNT, principal.getAccount());  // 获取账户信息
		info.put(TokenUtil.USER_NAME, principal.getUsername());  // 获取用户名
		info.put(TokenUtil.NICK_NAME, principal.getName());  // 获取昵称
		info.put(TokenUtil.REAL_NAME, principal.getRealName());  // 获取真实姓名
		info.put(TokenUtil.ROLE_NAME, principal.getRoleName());  // 获取角色名称
		info.put(TokenUtil.AVATAR, principal.getAvatar());  // 获取用户头像
		info.put(TokenUtil.LICENSE, TokenUtil.LICENSE_NAME);  // 获取许可证名称
		((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);  // 将附加信息设置到访问令牌中

		// token 状态设置
		if (jwtProperties.getState()) {
			// 如果 JWT 状态为 true,则增强访问令牌
			OAuth2AccessToken oAuth2AccessToken = jwtAccessTokenConverter.enhance(accessToken, authentication);
			String tokenValue = oAuth2AccessToken.getValue();  // 获取增强后的 token 值
			String tenantId = principal.getTenantId();  // 获取租户 ID
			String userId = Func.toStr(principal.getUserId());  // 获取用户 ID
			// 将访问令牌存储到 JwtUtil 中,以便后续管理
			JwtUtil.addAccessToken(tenantId, userId, tokenValue, accessToken.getExpiresIn());
		}

		return accessToken;  // 返回增强后的访问令牌
	}
}

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

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

相关文章

【NoSQL】portswigger NoSQL注入 labs 全解

目录 NoSQL NoSQL 数据库模型 NoSQL 注入的类型 NoSQL 语法注入 检测 MongoDB 中的语法注入 lab1:检测 NoSQL 注入 NoSQL 运算符注入 提交查询运算符 检测 MongoDB 中的运算符注入 lab2:利用 NoSQL 运算符注入绕过身份验证 利用语法注入来提取数据 MongoDB 中的数据…

华为---MUX VLAN简介及示例配置

目录 1. 产生背景 2. 应用场景 3. 主要功能 4. 基本概念 5. 配置步骤及相关命令 6.示例配置 6.1 示例场景 6.2 网络拓扑图 6.3 配置代码 6.4 配置及解析 6.5 测试验证 配置注意事项 1. 产生背景 MUX VLAN&#xff08;Multiplex VLAN&#xff09;提供了一种通过VLA…

【C++力扣】917.仅仅反转字母|387.字符串中第一个唯一字符|415.字符串相加

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山冈&#xff01; &#x1f525; 所属专栏&#xff1a;C深入学习笔记 &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 一、917.仅仅反转字母 1.1 题目描述…

VMware tools菜单为灰色无法安装

这个工具之前为灰色&#xff0c;无法安装&#xff0c;导致无法实现跟主机的共享文件夹等操作。极为不便。 根据其他教程提示&#xff1a;看到软件是这个配置。 修改为自动检测&#xff0c;tools就可以安装了。之前没注意到。 也有说dvd光盘也要设置。但是经过我测试。只设置软…

各省份消费差距(城乡差距)数据(2005-2022年)

消费差距&#xff0c;特别是城乡消费差距&#xff0c;是衡量一个国家或地区经济发展均衡性的重要指标。 2005年-2022年各省份消费差距&#xff08;城乡差距&#xff09;数据&#xff08;大数据&#xff09;.zip资源-CSDN文库https://download.csdn.net/download/2401_84585615/…

谷粒商城のRabbitMQ基础篇

文章目录 前言一、Rabbit MQ简介1、基本概念2、组件架构 二、使用步骤1.引入依赖2.application.properties3、docker 安装Rabbit MQ3、使用案例3.1、定义队列3.2、定义交换机3.3、绑定3.4、发送消息3.5、接受消息3.5、自定义消息序列化方式3.6、演示Fanout 交换机模式3.7、演示…

总结TypeScript相关知识

目录 引入认识特点安装使用变量声明类型推导 JS 和 TS 共有类型number类型boolean类型string类型Array类型null和undefined类型object类型symbol类型对象类型函数类型 可选和只读type 和 interface索引签名类型断言非空类型断言类型缩小严格赋值检测现象TS 新增类型字面量类型a…

[统计分析] 出现典型锯齿图的一种情况;资源泄露

接上回说&#xff0c;https://mzhan017.blog.csdn.net/article/details/142689870&#xff1b; 在问题分析的过程中发现产品里的一个统计计数出现了下面的锯齿型。而这个问题的表象之一是每隔一段时间&#xff0c;业务程序重启。所以产生这个锯齿形的原因之一就是业务程序重启&…

【C++ STL算法】二分查找 lower_bound、upper_bound、equal_range、binary_search

文章目录 【 1. 首个不小于 lower_bound 】【 2. 首个大于 upper_bound 】【 3. 所有等于 equel_range 】【 4. 二分查找 binary_search 】 当 指定区域内的数据处于有序状态 时&#xff0c;如果想查找某个目标元素&#xff0c;更推荐使用二分查找的方法&#xff08;相比顺序查…

openpnp - juki吸嘴尺寸

文章目录 openpnp - juki吸嘴尺寸概述笔记吸嘴可以对应的最小元件尺寸END openpnp - juki吸嘴尺寸 概述 在网上买的juki吸嘴的商品页面&#xff0c;并没有具体的吸嘴尺寸。 现在贴片时&#xff0c;要根据吸嘴外径大小来决定具体元件要用哪种吸嘴&#xff0c;先自己量一下。 …

研究生系统化入门教程(四)【机器学习】分类算法:决策树(信息熵,信息增益);集成学习方法之随机森林:估计器的工作流程是什么?为何采用BootStrap抽样?

“一般人都不是他们想要做的那种人,而是他们不得不做的那种人。——毛姆《月亮与六便士》” 🎯作者主页: 追光者♂🔥 🌸个人简介: 📝[1] CSDN 博客专家📝 🏆[2] 人工智能领域优质创作者🏆 🌟[3] 2022年度博客之星人工智能领域TOP4🌟 �…

鸿蒙next开发第一课03.ArkTs语法介绍-案例

前面已经学习了ArkTs的基本语法和DevEcoStudio的基本操作&#xff0c;接下来按照官方提示开发一个基本案例。 该案例是系统自带的demo&#xff0c;下载下来源代码后可以直接运行。 接下来我来演示如何运行demo。我在demo中加入了自己的注释。 切记&#xff1a;文件夹不能有中…

Crypto虐狗记---”你“和小鱼(七)

前言&#xff1a;剧情七 提示&#xff1a; 下载&#xff1a; 脚本&#xff1a; cyberpeace{125631357777427553} RSA算法_百度百科 (baidu.com)

VMware Tools 安装和配置

1. 使用 ISO 映射文件&#xff0c;并且选择.iso文件 2. 启动虚拟机&#xff0c;如果 VMware Tools 是灰色的&#xff0c;那么卸载 open-vm-tools&#xff08;不要重装&#xff09;&#xff0c;重新启动虚拟机。卸载可以参考&#xff1a;重装 open-vm-tools-CSDN博客 3. 拷贝挂载…

[C++]使用纯opencv部署yolov8-cls图像分类onnx模型

【算法介绍】 使用纯OpenCV部署YOLOv8-cls图像分类ONNX模型涉及几个关键步骤。 首先&#xff0c;你需要将YOLOv8-cls模型从PyTorch格式转换为ONNX格式&#xff0c;这是为了确保模型在不同深度学习框架之间的互操作性。这个转换过程通常是通过ultralytics框架中的model.export…

请求响应-08.响应-案例

一.案例 获取员工数据&#xff0c;返回统一响应结果&#xff0c;在页面上渲染展示 二.展示最终效果 三.步骤 步骤一&#xff1a; <dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3<…

C语言 | Leetcode C语言题解之第460题LFU缓存

题目&#xff1a; 题解&#xff1a; /* 数值链表的节点定义。 */ typedef struct ValueListNode_s {int key;int value;int counter;struct ValueListNode_s *prev;struct ValueListNode_s *next; } ValueListNode;/* 计数链表的节点定义。 其中&#xff0c;head是数值链表的头…

【Canvas与色彩】十六等分多彩隔断圆环

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>隔断圆环Draft5十六等分多彩</title><style type"text…

MyBatis-MP这个ORM框架强过你写的100行SQL,操作简单到不敢相信

MyBatis-MP这个ORM框架强过你写的100行SQL&#xff0c;操作简单到不敢相信 在繁杂的 Java 项目中&#xff0c;如何优雅、高效地进行数据库操作&#xff1f;MyBatis-MP&#xff0c;一个基于 MyBatis 的轻量级 ORM 框架&#xff0c;或许就是你的救星&#xff01;本文将介绍 MyBat…

Android车载——VehicleHal运行流程(Android 11)

1 概述 本篇主要讲解VehicleHal的主要运行流程&#xff0c;包括设置属性、获取属性、订阅属性、取消订阅、持续上报属性订阅等。 2 获取属性流程 2.1 获取属性流程源码分析 作为服务注册到hwServiceManager中的类是VehicleHalManager&#xff0c;所以&#xff0c;CarServic…