十一、Spring Boot:使用JWT实现用户认证深度解析
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。
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 认证。
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 上下文中。
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。
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 实现类,用于加载用户信息。
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,提升应用的安全性。
相关文章:
十一、Spring Boot:使用JWT实现用户认证深度解析
Spring Boot JWT(JSON Web Token):无状态认证 在现代 Web 开发中,无状态认证是一种重要的安全机制,它允许服务器在不存储会话信息的情况下验证用户身份。JSON Web Token(JWT)是一种常用的无状态…...
涨薪技术|持续集成Git使用详解
Git介绍 Git 是一个开源的分布式版本控制系统,用以有效、高速的处理从很小到非常大的项目版本管理。 Git 的特点: 分支更快、更容易。 支持离线工作;本地提交可以稍后提交到服务器上。 Git 提交都是原子的,且是整个项目范围的,…...
批量对 Word 优化与压缩,减少 Word 文件大小
在编辑 Word 文档的时候,我们通常会插入一些图片或者一些样式,这可能会导致 Word 文档的体积变得非常的庞大,不利于我们对 Word 文档进行分享、传输或者存档等操作,因此我们通常会碰到需要优化或者压缩 Word 文档的需求。那如何才…...
CSS定位详解上
1. 相对定位 1.1 如何设置相对定位? 给元素设置 position:relative 即可实现相对定位。 可以使用 left 、 right 、 top 、 bottom 四个属性调整位置。 1.2 相对定位的参考点在哪里? 相对自己原来的位置 1.3 相对定位的特点࿱…...
DeepSeek、Grok 和 ChatGPT 对比分析:从技术与应用场景的角度深入探讨
文章目录 一、DeepSeek:知识图谱与高效信息检索1. 核心技术2. 主要特点3. 应用场景4. 实际案例 二、Grok:通用人工智能框架1. 核心技术2. 主要特点3. 应用场景4. 实际案例 三、ChatGPT:聊天机器人与通用对话系统1. 核心技术2. 主要特点3. 应用…...
Unix Domain Socket和eventfd
在Linux开发中,Unix Domain Socket和eventfd是两种不同的通信机制,它们的设计目标和适用场景有显著差异。以下分点解释并配合示例说明: 一、Unix Domain Socket(UDS) 1. 是什么? 一种**本地进程间通信&am…...
【万字长文】基于大模型的数据合成(增强)及标注
写在前面 由于合成数据目前是一个热门的研究方向,越来越多的研究者开始通过大模型合成数据来丰富训练集,为了能够从一个系统的角度去理解这个方向和目前的研究方法便写了这篇播客,希望能对这个领域感兴趣的同学有帮助! 欢迎点赞&…...
2025年能源工作指导意见
2025年是“十四五”规划收官之年,做好全年能源工作意义重大。为深入贯彻落实党中央、国务院决策部署,以能源高质量发展和高水平安全助力我国经济持续回升向好,满足人民群众日益增长的美好生活用能需求,制定本意见。 一、总体要求…...
【Elasticsearch】Elasticsearch 中使用 HDFS 存储快照
在 Elasticsearch 中使用 HDFS 存储快照的步骤如下: 1.安装 HDFS 插件 要使用 HDFS 存储 Elasticsearch 的索引快照,需要在 Elasticsearch 集群的所有节点上安装 HDFS 插件。 • 在线安装:适用于网络环境良好的场景,执行以下命…...
Oracle 数据库基础入门(四):分组与联表查询的深度探索(下)
在 Oracle 数据库的操作中,联合查询与子查询是获取复杂数据的关键手段。当单表数据无法满足业务需求时,联合查询允许我们从多张表中提取关联信息,而子查询则能以嵌套的方式实现更灵活的数据筛选。对于 Java 全栈开发者而言,掌握这…...
深搜专题6:迷宫问题
描述 设有一个N*N方格的迷宫,入口和出口分别在左上角和右上角。 迷宫格子中分别放有0和1,0表示可通,1表示不能,迷宫走的规则如下: 即从某点开始,有八个方向可走,前进方格中数字为0时表示可通过…...
【每日学点HarmonyOS Next知识】web滚动、事件回调、selectable属性、监听H5内部router、Grid嵌套时高度设置
【每日学点HarmonyOS Next知识】web滚动、事件回调、selectable属性、监听H5内部router、Grid嵌套时高度设置 1、HarmonyOS WebView加载url无法滚动? scroll 里面嵌套webView,demo参考: // xxx.ets import web_webview from ohos.web.webv…...
MacBook上API调⽤⼯具推荐
在当今的软件开发中,API调用工具已经成为了开发者不可或缺的助手。无论是前端、后端还是全栈开发,API的调试、测试和管理都是日常工作中的重要环节。想象一下,如果没有这些工具,开发者可能需要手动编写复杂的CURL命令,…...
如何构建一个 Docker 镜像?
1. 创建 Dockerfile 文件 (1)选择工作目录 首先,创建一个项目目录,并进入该目录: mkdir my-docker-project cd my-docker-project(2)创建 Dockerfile 使用任何文本编辑器(如 nano、…...
【数据结构】LRUCache|并查集
目录 一、LRUCache 1.概念 2.实现:哈希表双向链表 3.JDK中类似LRUCahe的数据结构LinkedHashMap 🔥4.OJ练习 二、并查集 1. 并查集原理 2.并查集代码实现 3.并查集OJ 一、LRUCache 1.概念 最近最少使用的,一直Cache替换算法 LRU是Least Recent…...
go数组的声明和初始化
1.数组简介 数组是可以存放多个同一类型的数据。数组也是一种数据类型,在go中,数组是值类型。数组的长度也是数组类型的一部分,所以[2]int和[3]int属于不同的数据类型。 2.数组的长度也是类型的一部分 var arr1 [2]intvar arr2 [3]intfmt.P…...
基于STM32的智能家居中控系统
基于STM32的智能家居中控系统 下载源文件 链接:博客 第1章 绪论 1.1 研究背景与意义(扩增至1500字) • 市场数据支撑:引用IDC报告数据显示,中国智能家居设备市场年增长率达25%(2022年市场规模超6500亿元) …...
初识Qt · 信号与槽 · 基础知识
目录 前言: 信号和槽初识 两个问题 前言: 本文我们正式开始介绍信号与槽这个概念,在谈及Qt中的信号与槽这个概念之前,我们不妨回顾一下Linux中的信号,比如发生了除0错误,OS就会给该进程发送一个信号&am…...
Java高频面试之集合-03
hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶 面试官:说说ArrayList和LinkedList的区别 ArrayList 与 LinkedList 的详细对比 一、底层数据结构 特性ArrayListLinkedList存…...
常用的分布式ID设计方案
常用的分布式ID设计方案 在分布式系统中,生成全局唯一的ID是一个常见的需求。无论是数据库表中的主键,还是消息队列的消息ID,都需要一个高效且可靠的唯一标识符。本文将探讨几种常用的分布式ID设计方案,并分析它们的优缺点。 1. …...
宇树科技再落一子!天羿科技落地深圳,加速机器人创世纪
2025年3月5日,机器人行业龙头宇树科技(Unitree)在深圳再添新动作——全资子公司深圳天羿科技有限公司正式成立。这家注册资本10万元、法定代表人周昌慧的新公司,聚焦智能机器人研发与销售,标志着宇树科技在华南市场的战…...
【长安大学】苹果手机/平板自动连接认证CHD-WIFI脚本(快捷指令)
背景: 已经用这个脚本的记得设置Wifi时候,关闭“自动登录” 前几天实在忍受不了CHD-WIFI动不动就断开,一天要重新连接,点登陆好几次。试了下在网上搜有没有CHD-WIFI的自动连接WIFI自动认证脚本,那样我就可以解放双手&…...
基于遗传算法的无人机三维路径规划仿真步骤详解
基于遗传算法的无人机三维路径规划仿真步骤详解 一、问题定义 目标:在三维空间内,寻找从起点到终点的最优路径,需满足: 避障:避开所有障碍物。路径最短:总飞行距离尽可能短。平滑性:转折角度不宜过大,降低机动能耗。输入: 三维地图(含障碍物,如立方体、圆柱体)。起…...
【Elasticsearch】索引生命周期管理相关的操作(Index Lifecycle Actions)
Elasticsearch 的Index Lifecycle Management(ILM)是一种用于管理索引生命周期的工具,它允许用户根据索引的使用阶段(如热、温、冷、冻结)自动执行一系列操作。以下是详细解释 Elasticsearch 中的索引生命周期操作(Index Lifecycl…...
计算机毕业设计SpringBoot+Vue.js电商平台(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
【杂谈】信创电脑华为w515(统信系统)登录锁定及忘记密码处理
华为w515麒麟芯片版,还有非麒麟芯片版本,是一款信创电脑,一般安装的UOS系统。 准备一个空U盘,先下载镜像文件及启动盘制作工具,连接如下: 百度网盘 请输入提取码 http://livecd.uostools.com/img/apps/l…...
初始提示词(Prompting)
理解LLM架构 在自然语言处理领域,LLM(Large Memory Language Model,大型记忆语言模型)架构代表了最前沿的技术。它结合了存储和检索外部知识的能力以及大规模语言模型的强大实力。 LLM架构由外部记忆模块、注意力机制和语…...
Vue+el-upload配置minIO实现大文件的切片并发上传、上传进度展示、失败重试功能
vue3el-upload实现切片上传 效果图 初始界面 上传中的界面 上传完成的界面 上传失败的界面 <template><div><el-uploadclass"BigFileUpload"ref"uploadRef"action"#"drag:show-file-list"false":on-change"…...
正则表达式梳理(基于python)
正则表达式(regular expression)是一种针对字符串匹配查找所定义的规则模式,独立于语言,但不同语言在实现上也会存在一些细微差别,下面基于python对常用的相关内容进行梳理。 文章目录 一、通用常识1.通配符ps.反义 2.…...
Scala 中 val 和对象内部状态的关系
在 Scala 中,val 用于声明不可变的变量,这意味着一旦 val 被赋值,它的引用(即指向的内存地址)就不能再改变。然而,这并不影响对象内部的状态(即对象的属性)是否可以改变。具体来说&a…...
