十一、Spring Boot:使用JWT实现用户认证深度解析

devtools/2025/3/6 2:16:03/

Spring Boot JWT(JSON Web Token):无状态认证

在现代 Web 开发中,无状态认证是一种重要的安全机制,它允许服务器在不存储会话信息的情况下验证用户身份。JSON Web Token(JWT)是一种常用的无状态认证技术,它通过一个紧凑的 URL 安全令牌来传递用户身份信息。Spring Boot 与 JWT 的结合可以为应用提供强大的安全保护。本文将详细介绍如何在 Spring Boot 中集成 JWT 实现无状态认证。

一、JWT 概述

1.1 什么是 JWT

JWT 是一种基于 JSON 的开放标准(RFC 7519),用于在各方之间传递声明信息。它由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。JWT 的主要特点是无状态、自包含和安全,非常适合用于分布式系统和微服务架构中的身份验证和信息交换。

1.2 JWT 的工作原理

当用户使用用户名和密码登录系统时,认证服务器会验证用户的身份。如果验证成功,服务器会生成一个 JWT,并将其返回给客户端。客户端在后续的请求中,将 JWT 放在请求头的 Authorization 字段中,格式为 Bearer <token>。服务器接收到请求后,会验证 JWT 的合法性,如果验证通过,则允许用户访问受保护的资源。

二、Spring Boot 集成 JWT

2.1 添加依赖

在 Spring Boot 项目中,需要在 pom.xml 文件中添加 JWT 和 Spring Security 的依赖。

<!-- Spring Security -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency><!-- JWT -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

2.2 配置 JWT 工具类

创建一个工具类 JwtUtil,用于生成和验证 JWT。

java">import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.Date;
import java.util.HashMap;
import java.util.Map;@Component
public class JwtUtil {@Value("${jwt.secret}")private String secret;public String generateToken(String username) {Map<String, Object> claims = new HashMap<>();return Jwts.builder().setClaims(claims).setSubject(username).setIssuedAt(new Date(System.currentTimeMillis())).setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 小时有效期.signWith(SignatureAlgorithm.HS256, secret).compact();}public String extractUsername(String token) {return extractClaim(token, Claims::getSubject);}public Date extractExpiration(String token) {return extractClaim(token, Claims::getExpiration);}public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {final Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();return claimsResolver.apply(claims);}public Boolean isTokenExpired(String token) {return extractExpiration(token).before(new Date());}public Boolean validateToken(String token, String username) {final String extractedUsername = extractUsername(token);return (extractedUsername.equals(username) && !isTokenExpired(token));}
}

2.3 配置 Spring Security

创建一个 SecurityConfig 类,配置 Spring Security 以支持 JWT 认证。

java">import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate JwtRequestFilter jwtRequestFilter;@Autowiredprivate UserDetailsService userDetailsService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers("/api/authenticate").permitAll().anyRequest().authenticated().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);}@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}

2.4 创建 JWT 请求过滤器

创建一个 JwtRequestFilter 类,用于在每个请求中解析 JWT,并将其设置到 Spring Security 上下文中。

java">import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Component
public class JwtRequestFilter extends OncePerRequestFilter {@Autowiredprivate JwtUtil jwtUtil;@Autowiredprivate UserDetailsService userDetailsService;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException {final String authorizationHeader = request.getHeader("Authorization");String username = null;String jwt = null;if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {jwt = authorizationHeader.substring(7);username = jwtUtil.extractUsername(jwt);}if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {UserDetails userDetails = userDetailsService.loadUserByUsername(username);if (jwtUtil.validateToken(jwt, userDetails)) {UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);}}chain.doFilter(request, response);}
}

2.5 创建认证和授权接口

创建一个 AuthenticationController 类,用于处理用户登录和生成 JWT。

java">import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/api")
public class AuthenticationController {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate JwtUtil jwtUtil;@Autowiredprivate UserDetailsService userDetailsService;@PostMapping("/authenticate")public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) throws Exception {try {authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword()));} catch (BadCredentialsException e) {throw new Exception("Incorrect username or password", e);}final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());final String jwt = jwtUtil.generateToken(userDetails.getUsername());return ResponseEntity.ok(new AuthenticationResponse(jwt));}
}

2.6 创建用户详情服务

创建一个 UserDetailsService 实现类,用于加载用户信息。

java">import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;import java.util.ArrayList;@Service
public class CustomUserDetailsService implements UserDetailsService {@Autowiredprivate UserRepository userRepository;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username));return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>());}
}

2.7 配置 application.properties

application.properties 文件中配置 JWT 密钥。

jwt.secret=yourSecretKey

2.8 测试

启动应用后,可以通过发送 POST 请求到 /api/authenticate 接口进行登录,获取 JWT。然后在请求头中添加 Authorization: Bearer <token>,即可访问受保护的接口。

三、总结

通过本文的介绍,我们详细学习了如何在 Spring Boot 中集成 JWT 实现无状态认证。我们添加了必要的依赖,配置了 JWT 工具类、Spring Security、JWT 请求过滤器、认证和授权接口以及用户详情服务。希望这些内容能够帮助你在实际开发中更好地应用 JWT,提升应用的安全性。


http://www.ppmy.cn/devtools/164883.html

相关文章

React进阶之前端业务Hooks库(六)

前端业务Hooks库 请求useRequest题外:主流大厂编码规范的要求:目录详解useRequest.tsuseRequestImplement.tsFetch.ts插件的实现src/useRetryPlugin.tssrc/useAutoRunPlugin.ts请求失败的日志上报请求 useRequest 为什么不使用axios而使用useRequest 因为 axios 在 vue 项目中…

Stable Diffusion模型采样方法与参数配置详解(含步数及画风适配表)

Stable Diffusion模型采样方法与参数配置详解&#xff08;含步数及画风适配表&#xff09; 以下为当前主流采样方法的性能对比及参数配置建议&#xff0c;结合显存占用、生成速度、适用场景等维度分类总结&#xff1a; 一、采样方法对比表 采样方法推荐步数显存占用生成速度…

C语言文件操作学习笔记:从基础到实践

在C语言的知识体系中&#xff0c;文件操作是极为关键的一环&#xff0c;它赋予了程序存储和读取外部数据的能力&#xff0c;对于开发各类实用程序至关重要。近期&#xff0c;借助课程的学习&#xff0c;我对C语言文件操作进行了系统且深入的学习&#xff0c;下面将我的学习心得…

vmware虚拟机安装银河麒麟高级服务器操作系统V10

文档时间&#xff1a;2025年03月 安装环境 vmware虚拟机版本&#xff1a;VMware Workstation 17 镜像版本&#xff1a;Kylin-Server-V10-SP3-2403-Release-20240426-x86_64.iso 镜像内核版本&#xff1a;4.19 镜像下载 镜像在官网下载&#xff0c;申请使用 官网&#xff1…

笔记:代码随想录算法训练营day36:LeetCode1049. 最后一块石头的重量 II、494. 目标和、474.一和零

学习资料&#xff1a;代码随想录 1049.最后一块石头的重量II 力扣题目链接 思路&#xff1a;如何讲该问题转化为背包问题&#xff1a;还是对半分去碰&#xff0c;对半分去碰碰剩下的就是最小的。然后背包容量就是一半儿&#xff0c;物品重量等于物品价值等于stones[i] 和上…

STM32---FreeRTOS中断管理试验

一、实验 实验目的&#xff1a;学会使用FreeRTOS的中断管理 创建两个定时器&#xff0c;一个优先级为4&#xff0c;另一个优先级为6&#xff1b;注意&#xff1a;系统所管理的优先级范围 &#xff1a;5~15 现象&#xff1a;两个定时器每1s&#xff0c;打印一段字符串&#x…

大语言模型揭秘:从诞生到智能

引言 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;无疑是技术领域最耀眼的明星之一。它们不仅能够理解人类的自然语言&#xff0c;还能生成流畅的文本&#xff0c;甚至在对话、翻译、创作等任务中表现出接近人类的智能…

基于Python Django的人脸识别上课考勤系统(附源码,部署)

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…