SpringSecurity + JWT 实现登录认证

目录

1. JWT 的组成和优势

2. JWT 的工作原理

2.1 生成 JWT

2.2 传输JWT

3. SpringSecurity + JWT 实现登录认证

3.1 配置 Spring Security 安全过滤链

3.2 自定义登录认证过滤器

3.3  实现SpringSecurity用户对象

3.4 获取当前登录用户


1. JWT 的组成和优势

JWT 由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)

  1. 头部(Header):包含了关于生成该 JWT 的信息以及所使用的算法类型。

  2. 载荷(Payload):包含了要传递的数据,例如身份信息和其他附属数据。

  3. 签名(Signature):使用密钥对头部和载荷进行签名,以验证其完整性。

JWT 相较于传统的 Session 认证机制,具有以下优势:

  1. 无需服务器存储状态:传统的基于 Session 认证机制需要服务器在会话中存储用户的状态信息,包括用户的登录状态、权限等。而使用 JWT,服务器无需存储任何会话状态信息,所有的认证和授权信息都包含在 JWT 中,使得系统可以更容易地进行水平扩展。

  2. 跨域支持:由于 JWT 包含了完整的认证和授权信息,因此可以轻松地在多个域之间进行传递和使用,实现跨域授权。

  3. 适应微服务架构:在微服务架构中,很多服务是独立部署并且可以横向扩展的,这就需要保证认证和授权的无状态性。使用 JWT 可以满足这种需求,每次请求携带 JWT 即可实现认证和授权。

  4. 自包含:JWT 包含了认证和授权信息,以及其他自定义的声明,这些信息都被编码在 JWT 中,在服务端解码后使用。JWT 的自包含性减少了对服务端资源的依赖,并提供了统一的安全机制。

  5. 扩展性:JWT 可以被扩展和定制,可以按照需求添加自定义的声明和数据,灵活性更高。

总结来说,使用 JWT 相较于传统的基于会话的认证机制,可以减少服务器存储开销和管理复杂性,实现跨域支持和水平扩展,并且更适应无状态和微服务架构。

2. JWT 的工作原理

JWT 工作原理包含三部分:

1. 生成 JWT

2. 传输 JWT

3. 验证 JWT

2.1 生成 JWT

在用户登录时,当服务器端验证了用户名和密码的正确性后,会根据用户的信息,如用户 ID 和用户名称,加上服务器端存储的 JWT 秘钥一起来生成一个 JWT 字符串,也就是我们所说的 Token.

@Value("${jwt.secret}")
private String jwtSecret;/*** 用户登录** @return {@link ResponseEntity }*/
@PostMapping("/login")
public ResponseEntity login(@Validated UserDto userDto, HttpServletRequest request) {// --- 验证图片验证码 ---// 验证用户名密码LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getUsername, userDto.getUsername());User user = userService.getOne(queryWrapper);if (user != null && passwordEncoder.matches(userDto.getPassword(), user.getPassword())) {// 生成 JWTHashMap<String, Object> payLoad = new HashMap<>() {{put(JWTConstant.JWT_UID_KEY, user.getUid());put(JWTConstant.JWT_USERNAME_KEY, user.getUsername());}};HashMap<String, String> result = new HashMap<>();result.put(JWTConstant.JWT_KEY, JWTUtil.createToken(payLoad, jwtSecret.getBytes()));result.put(JWTConstant.JWT_USERNAME_KEY, user.getUsername());return ResponseEntity.success(result);}return ResponseEntity.fail("用户名或密码不正确!");
}
jwt:secret: aicloud_springcloud_secret
public class JWTConstant {public static final String JWT_KEY = "jwt";public static final String JWT_UID_KEY = "uid";public static final String JWT_USERNAME_KEY = "username";public static final String JWT_TOKEN_KEY = "Authorization";
}

2.2 传输JWT

JWT 通常存储在客户端的 Cookie、LocalStorage、SessionStorage 等位置,客户端在每次请求时把 JWT 放在 Header 请求头中传递给服务器端.

存储 JWT 到 LocalStorage 

$.ajax({url: '/user/login',type: 'POST',data: field,success: function (res) {if (res.code === 200) {layui.data(localStorage_jwt_key, {// 将生成的 jwt 存储到 LocalStoragekey: jwt_token_key,value: res.data.jwt,});layui.data(localStorage_username_key, {// 存储 usernamekey: login_username_key,value: res.data.username,});// 刷新父页面 (登录页是个弹窗)window.parent.location.href = window.parent.location.href} else {layer.msg("登录失败:" + res.msg);}}
});
var localStorage_username_key = "login_username_key"
var localStorage_jwt_key = "jwt_token_key"
var login_username_key = "username"
var jwt_token_key = "authorization"

 登录成功后,其他请求的 header 中带上 token

function authAjax($, url, data, callback) {$.ajax({url: url,type: 'POST',headers: {'Authorization': layui.data("jwt_token_key").authorization},data: data,success: callback,});
}

3. SpringSecurity + JWT 实现登录认证

3.1 配置 Spring Security 安全过滤链

@Configuration
@EnableWebSecurity
public class SecurityConfig {@Resourceprivate LoginAuthenticationFilter loginAuthenticationFilter;/*** 加盐加密** @return {@link PasswordEncoder }*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}/*** 配置 Spring Security 安全过滤链** @param http* @return {@link SecurityFilterChain }* @throws Exception*/@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {return http// 禁用明文验证.httpBasic(AbstractHttpConfigurer::disable)// 禁用 CSRF 验证.csrf(AbstractHttpConfigurer::disable)// 禁用默认登录页.formLogin(AbstractHttpConfigurer::disable)// 禁用默认 header,支持 iframe 访问页面.headers(AbstractHttpConfigurer::disable)// 禁用默认注销页.logout(AbstractHttpConfigurer::disable)// 禁用 Session (默认使用 JWT 认证).sessionManagement(session ->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)).authorizeHttpRequests(auth -> auth// 允许访问的资源.requestMatchers("/layui/**","/js/**","/image/**","/login.html","/index.html","/reg.html","/user/login","/user/reg","/captcha/create","/discuss/delete","/discuss/detail","/kafka/**","/swagger-ui/**","/v3/**","/doc.html","/webjars/**").permitAll()// 其他请求都需要认证拦截.anyRequest().authenticated())// 添加自定义认证过滤器.addFilterBefore(loginAuthenticationFilter, UsernamePasswordAuthenticationFilter.class).build();}
}

3.2 自定义登录认证过滤器

@Component
public class LoginAuthenticationFilter extends OncePerRequestFilter {@Value("${jwt.secret}")private String jwtSecret;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 1.获取 JWT 令牌String token = request.getHeader(JWTConstant.JWT_TOKEN_KEY);if (!StringUtils.isBlank(token)) {// 2.判断 JWT 令牌正确性if (JWTUtil.verify(token, jwtSecret.getBytes())) {// 3.获取用户信息,存储 Security 中JWT jwt = JWTUtil.parseToken(token);if (ObjectUtil.isNotNull(jwt) && jwt.getPayload(JWTConstant.JWT_UID_KEY) != null&& jwt.getPayload(JWTConstant.JWT_USERNAME_KEY) != null) {Long uid = Long.parseLong(jwt.getPayload(JWTConstant.JWT_UID_KEY).toString());String username = jwt.getPayload(JWTConstant.JWT_USERNAME_KEY).toString();// 4.创建用户对象SecurityUserDetails userDetails = new SecurityUserDetails(uid, username, EMPTY);UsernamePasswordAuthenticationToken authentication =new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());// 绑定 request 对象authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(authentication);}}}filterChain.doFilter(request, response);}
}

3.3  实现SpringSecurity用户对象

@Data
@Builder
public class SecurityUserDetails implements UserDetails {@Serialprivate static final long serialVersionUID = -829716430599304080L;private Long uid;private String username;private String password;public SecurityUserDetails(Long uid, String username, String password) {this.uid = uid;this.username = username;this.password = password;}/*** 权限** @return {@link Collection }<{@link ? } {@link extends } {@link GrantedAuthority }>*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}/*** 密码** @return {@link String }*/@Overridepublic String getPassword() {return "";}/*** 用户名** @return {@link String }*/@Overridepublic String getUsername() {return "";}/*** 账户是否过期** @return boolean*/@Overridepublic boolean isAccountNonExpired() {return false;}/*** 账号是否锁定** @return boolean*/@Overridepublic boolean isAccountNonLocked() {return false;}/*** 密码是否过期** @return boolean*/@Overridepublic boolean isCredentialsNonExpired() {return false;}/*** 账号是否可用** @return boolean*/@Overridepublic boolean isEnabled() {return true;}
}

3.4 获取当前登录用户

public class SecurityUtil {/*** 获取当前登录用户** @return {@link SecurityUserDetails }*/public static SecurityUserDetails getCurrentUser() {SecurityUserDetails userDetails = null;try {userDetails = (SecurityUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();} catch (Exception e) {}return userDetails;}
}

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

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

相关文章

SSE(Server Sent Event)实战(3)- Spring Web Flux 实现

上篇博客 SSE&#xff08;Server Sent Event&#xff09;实战&#xff08;2&#xff09;- Spring MVC 实现&#xff0c;我们用 Spring MVC 实现了简单的消息推送&#xff0c;并且留下了两个问题&#xff0c;这篇博客&#xff0c;我们用 Spring Web Flux 实现&#xff0c;并且看…

STM32(六):STM32指南者-定时器实验

目录 一、基本概念1、常规定时器2、内核定时器 二、基本定时器实验1、实验说明2、编程过程&#xff08;1&#xff09;配置LED&#xff08;2&#xff09;配置定时器&#xff08;3&#xff09;设定中断事件&#xff08;4&#xff09;主函数计数 3、工程代码 三、通用定时器实验实…

【Neural signal processing and analysis zero to hero】- 2

Nonstationarities and effects of the FT course from youtube: 传送地址 why we need extinguish stationary and non-stationary signal, because most of neural signal is non-stationary. Welch’s method for smooth spectral decomposition Full FFT method y…

【TDA4板端部署】基于 Pytorch 训练并部署 ONNX 模型在 TDA4

1 将torch模型转onnx模型 Ti转换工具只支持以下格式&#xff1a; Caffe - 0.17 (caffe-jacinto in gitHub) Tensorflow - 1.12 ONNX - 1.3.0 (opset 9 and 11) TFLite - Tensorflow 2.0-Alpha 基于 Tensorflow、Pytorch、Caffe 等训练框架&#xff0c;训练模型&#xff1a;选择…

数据结构与算法(2):顺序表与链表

1.前言 哈喽大家好喔&#xff0c;今天博主继续进行数据结构的分享与学习&#xff0c;今天的主要内容是顺序表与链表&#xff0c;是最简单但又相当重要的数据结构&#xff0c;为以后的学习有重要的铺垫&#xff0c;希望大家一起交流学习&#xff0c;互相进步&#xff0c;让我们…

数据结构之跳表SkipList、ConcurrentSkipListMap

概述 SkipList&#xff0c;跳表&#xff0c;跳跃表&#xff0c;在LevelDB和Lucene中都广为使用。跳表被广泛地运用到各种缓存实现当中&#xff0c;跳跃表使用概率均衡技术而不是使用强制性均衡&#xff0c;因此对于插入和删除结点比传统上的平衡树算法更为简洁高效。 Skip lis…

AQS详解(详细图文)

目录 AQS详解1、AQS简介AbstractQueuedSynchronizer的继承结构和类属性AQS的静态内部类Node总结AQS的实现思想总结AQS的实现原理AQS和锁的关系 2、AQS的核心方法AQS管理共享资源的方式独占方式下&#xff0c;AQS获取资源的流程详解独占方式下&#xff0c;AQS释放资源的流程详解…

如何通过DBC文件看懂CAN通信矩阵

实现汽车CAN通信开发&#xff0c;必不可少要用到DBC文件和CAN通信矩阵。 CAN通信矩阵是指用于描述 CAN 网络中各个节点之间通信关系的表格或矩阵。它通常记录了每个节点能够发送和接收的消息标识符&#xff08;ID&#xff09;以及与其他节点之间的通信权限。 通信矩阵在 CAN 网…

利用Msfvenom获取WindowsShell

一、在kali主机上利用msfvenom生成windows端的安装程序(exe文件),程序名最好取一个大家经常安装的程序,如腾讯视频、爱奇艺等。 (1)由于生成的程序可能会被杀毒软件识别,我们比较一下使用单个编码器生成的程序与用两个编码器生成的程序,哪个更容易被识别。 利用单个编码…

SSE(Server Sent Event)实战(2)- Spring MVC 实现

一、服务端实现 使用 RestController 注解创建一个控制器类&#xff08;Controller&#xff09; 创建一个方法来创建一个客户端连接&#xff0c;它返回一个 SseEmitter&#xff0c;处理 GET 请求并产生&#xff08;produces&#xff09;文本/事件流 (text/event-stream) 创建…

mybatis-plus仿 JPA的 GenerationType.TABLE 实现

概述 1&#xff09;配置mybatis-plus 主键默认策略&#xff0c;实体添加主键注解 2&#xff09;新建主键存储表 3&#xff09;编写主键策略实现类 注意事项&#xff1a;如果 程序含数据库恢复功能&#xff0c;数据恢复后&#xff0c;需调用 CustomIdGenerator.clearTabIdState…

如何使用Milvus Cloud进行稀疏向量搜索

如何使用Milvus Cloud进行向量搜索Milvus Cloud 是一款高度可扩展、性能出色的开源向量数据库。在最新的 2.4 版本中,Milvus Cloud 支持了稀疏和稠密向量(公测中)。本文将利用 Milvus Cloud 2.4 来存储数据集并执行向量搜索。 接下来,我们将演示如何利用 Milvus Cloud 在 M…

[GXYCTF2019]Ping Ping Ping1

打开靶机 结合题目名称&#xff0c;考虑是命令注入&#xff0c;试试ls 结果应该就在flag.php。尝试构造命令注入载荷。 cat flag.php 可以看到过滤了空格,用 $IFS$1替换空格 还过滤了flag&#xff0c;我们用字符拼接的方式看能否绕过,ag;cat$IFS$1fla$a.php。注意这里用分号间隔…

睡前故事—绿色科技的未来:可持续发展的梦幻故事

欢迎来到《Bedtime Stories Time》。这是一个我们倾听、放松、并逐渐入睡的播客。感谢你收听并支持我们&#xff0c;希望你能将这个播客作为你睡前例行活动的一部分。今晚我们将讲述绿色科技的未来&#xff1a;可持续发展的梦幻故事的故事。一个宁静的夜晚&#xff0c;希望你现…

0602STM32定时器输出比较

STM32定时器输出比较 PWM简介 主要用来输出PWM波形&#xff0c;PWM波形又是驱动电机的必要条件&#xff0c;所以如果想用STM32做一些有电机的项目&#xff0c;比如智能车&#xff0c;机器人等。那输出比较功能就要认真掌握 1.PWM驱动LED呼吸灯 2.PWM驱动舵机 3.PWM驱动直流电机…

vue中获取剪切板中的内容

目录 1.说明 2.示例 3.总结 1.说明 在系统中的画面或者时外部文件中进行拷贝处理后&#xff0c;在页面中可以获取剪切板的内容。 2.示例 方式①(直接获取) // 异步函数获取剪切板内容 async function getClipboardContent(ev: any) {try {ev.preventDefault()const clip…

搜维尔科技:【研究】触觉技术将在5年内以8种方式改变人们的世界

触觉技术在过去几年中发展迅猛&#xff0c;大大提高了反馈的精确度和真实度。其应用产生了真正的影响&#xff0c;数百家公司和企业都集成了触觉技术来增强培训和研究模拟。 虽然触觉技术主要用于 B2B 层面&#xff0c;但触觉技术可能会彻底改变我们的生活&#xff0c;尤其是通…

DLMS协议中的高级安全(HLS)身份验证

1.四步身份验证协议 在IEC 62056-53中已说明&#xff0c;ACSE提供部分高级身份安全&#xff08;HLS&#xff09;验证服务。高级身份安全验证适用于通信通道不能提供内部安全&#xff0c;应采取防范措施以防止偷听和信息&#xff08;密码&#xff09;重现的情况。这时&#xff…

视频共享融合赋能平台LntonCVS视频监控业务平台技术方案详细介绍

LntonCVS国标视频综合管理平台是一款智慧物联应用平台&#xff0c;核心技术基于视频流媒体&#xff0c;采用分布式和负载均衡技术开发&#xff0c;提供广泛兼容、安全可靠、开放共享的视频综合服务。该平台功能丰富&#xff0c;包括视频直播、录像、回放、检索、云存储、告警上…

【数据结构】详解堆

一、堆的概念 堆(Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵 完全二叉树的 数组对象。 堆是非线性数据结构&#xff0c;相当于一维数组&#xff0c;有两个直接后继。 如果有一个关键码的集合K { k₀&#xff0c;k₁&#xff0c;k₂ &#xff0…