Spring Boot与JWT深度整合:实现高效安全认证机制的详细指南

news/2025/3/24 23:50:20/文章来源:https://www.cnblogs.com/java-note/p/18787761

一、Spring Boot 与 JWT 的概述

(一)Spring Boot 简介

Spring Boot 是一个开源的 Java 基于 Spring 框架的快速开发平台,它极大地简化了 Spring 应用的开发过程。通过自动配置 Spring 和第三方库,Spring Boot 让开发者能够快速搭建出一个功能强大的应用。它提供了大量的 “Starters”(启动器),这些启动器封装了各种常用的功能,例如数据库访问、安全认证、Web 开发等,开发者只需在项目中引入相应的依赖即可。

Spring Boot 的核心优势包括:

  1. 快速开发:通过自动配置,减少了大量的配置文件编写工作。
  2. 独立运行:Spring Boot 应用内嵌了 Tomcat、Jetty 等服务器,无需部署 WAR 文件。
  3. 简化依赖管理:通过 Maven 或 Gradle 的依赖管理,简化了依赖的配置和版本管理。
  4. 易于监控:提供了丰富的监控接口,方便对应用进行监控和管理。

(二)JWT 简介

JWT(JSON Web Token)是一种开放标准(RFC 7519),它允许在客户端和服务器之间以 JSON 对象安全地传递信息。JWT 的核心优势在于它的无状态性和可扩展性。由于 JWT 包含了用户的身份信息,服务器在验证用户身份时无需查询数据库,从而提高了系统的性能和可扩展性。JWT 通常用于身份认证和信息交换,它通过数字签名确保数据的完整性和安全性。

JWT 的结构包括三部分:

  1. Header(头部):通常包含令牌的类型(如 JWT)和所使用的签名算法(如 HMAC SHA256 或 RSA)。
  2. Payload(载荷):包含声明(Claims),这些声明是关于实体(通常是用户)和其他数据的声明。声明分为三种类型:
  • Registered Claims(注册声明):预定义的声明,如 iss(发行人)、exp(过期时间)、sub(主题)等。
  • Public Claims(公共声明):自定义的声明,开发者可以根据需求添加任何信息。
  • Private Claims(私有声明):自定义的声明,通常用于存储用户的身份信息。
  1. Signature(签名):用于验证消息的完整性和确保消息自签发后未被篡改。

二、Spring Boot 与 JWT 的结合优势

(一)无状态认证

JWT 是一种无状态的认证机制,它将用户的身份信息存储在令牌中,服务器无需保存用户的会话信息。这种方式使得系统更加轻量级,同时也便于扩展。在分布式系统中,无状态认证可以减少服务器之间的通信开销,提高系统的性能。

(二)安全性高

JWT 使用数字签名技术对令牌进行加密,确保令牌在传输过程中不会被篡改。此外,JWT 支持多种加密算法,开发者可以根据需求选择合适的加密方式。常见的加密算法包括:

  • HMAC SHA256:使用密钥进行签名,适合对称加密。
  • RSA:使用公钥和私钥进行签名,适合非对称加密。

(三)易于扩展

由于 JWT 是无状态的,它可以在分布式系统中轻松实现单点登录(SSO)。多个服务可以共享同一个 JWT,从而实现无缝的用户认证。这种方式特别适合微服务架构,每个服务都可以独立验证 JWT,而无需依赖中央认证服务器。

(四)跨域支持

JWT 令牌可以作为 HTTP 请求头的一部分发送,这种方式使得 JWT 在跨域请求中非常方便。客户端可以在跨域请求中携带 JWT,服务器通过验证 JWT 来确认用户的身份,从而实现跨域认证。

三、Spring Boot 与 JWT 的整合实现

(一)项目搭建与依赖准备

1. 创建 Spring Boot 项目

使用 Spring Initializr(https://start.spring.io/)快速创建一个 Spring Boot 项目。选择以下依赖:

  • Spring Web
  • Spring Security(可选,用于增强安全性)
  • Lombok(可选,用于简化代码)

2. 添加 JWT 相关依赖

pom.xml 文件中添加 JWT 相关的依赖:

<dependencies><!-- Spring Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- JWT --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.5</version><scope>runtime</scope></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>

(二)JWT 配置类

在 Spring Boot 项目中,可以通过配置类来管理 JWT 的相关参数。以下是一个简单的 JWT 配置类示例:

package com.example.config;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;@Component
@ConfigurationProperties(prefix = "config.jwt")
public class JwtConfig {private String secret; // 加密密钥private long expire; // token 有效时长private String header; // header 名称public String createToken(String subject) {Date nowDate = new Date();Date expireDate = new Date(nowDate.getTime() + expire * 1000);return Jwts.builder().setHeaderParam("typ", "JWT").setSubject(subject).setIssuedAt(nowDate).setExpiration(expireDate).signWith(SignatureAlgorithm.HS512, secret).compact();}public Claims getTokenClaim(String token) {try {return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();} catch (Exception e) {return null;}}public boolean isTokenExpired(Date expirationTime) {return expirationTime.before(new Date());}public Date getExpirationDateFromToken(String token) {return getTokenClaim(token).getExpiration();}public String getUsernameFromToken(String token) {return getTokenClaim(token).getSubject();}public Date getIssuedAtDateFromToken(String token) {return getTokenClaim(token).getIssuedAt();}// Getter 和 Setter 方法
}

application.properties 文件中配置 JWT 相关参数:

config.jwt.secret=yourSecretKey
config.jwt.expire=3600
config.jwt.header=Authorization

(三)JWT 工具类

JWT 工具类用于生成和验证 JWT 令牌。以下是一个简单的 JWT 工具类示例:

package com.example.util;import com.example.config.JwtConfig;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.Date;@Component
public class JwtUtil {@Autowiredprivate JwtConfig jwtConfig;public String generateToken(String username) {Date now = new Date();Date expiryDate = new Date(now.getTime() + jwtConfig.getExpire() * 1000);return Jwts.builder().setSubject(username).setIssuedAt(now).setExpiration(expiryDate).signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret()).compact();}public String getUsernameFromToken(String token) {Claims claims = Jwts.parser().setSigningKey(jwtConfig.getSecret()).parseClaimsJws(token).getBody();return claims.getSubject();}public boolean validateToken(String token) {try {Jwts.parser().setSigningKey(jwtConfig.getSecret()).parseClaimsJws(token);return true;} catch (Exception e) {return false;}}
}

(四)JWT 拦截器

为了在每个请求中验证 JWT,可以创建一个拦截器。拦截器会在每个请求到达控制器之前验证 JWT 的有效性。

package com.example.interceptor;import com.example.config.JwtConfig;
import com.example.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
public class JwtInterceptor implements HandlerInterceptor {@Autowiredprivate JwtUtil jwtUtil;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {final String authHeader = request.getHeader(JwtConfig.HEADER);if (authHeader != null && authHeader.startsWith("Bearer ")) {final String token = authHeader.substring(7);if (jwtUtil.validateToken(token)) {return true;}}response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");return false;}
}

在 Spring Boot 的配置类中注册拦截器:

package com.example.config;import com.example.interceptor.JwtInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate JwtInterceptor jwtInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtInterceptor).addPathPatterns("/api/**") // 拦截所有 /api 下的请求.excludePathPatterns("/api/auth/**"); // 排除登录接口}
}

(五)用户认证与 JWT 生成

在用户登录时,验证用户的用户名和密码,如果验证通过,则生成 JWT 并返回给客户端。

package com.example.controller;import com.example.entity.User;
import com.example.service.UserService;
import com.example.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class AuthController {@Autowiredprivate UserService userService;@Autowiredprivate JwtUtil jwtUtil;@PostMapping("/api/auth/login")public ResponseEntity<?> login(@RequestBody User user) {User authenticatedUser = userService.authenticate(user.getUsername(), user.getPassword());if (authenticatedUser != null) {String token = jwtUtil.generateToken(authenticatedUser.getUsername());return ResponseEntity.ok().header("Authorization", "Bearer " + token).body("Login successful");} else {return ResponseEntity.status(401).body("Invalid credentials");}}
}

(六)用户服务类

用户服务类用于处理用户相关的逻辑,例如用户认证。

package com.example.service;import com.example.entity.User;
import com.example.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public User authenticate(String username, String password) {User user = userRepository.findByUsername(username);if (user != null && user.getPassword().equals(password)) {return user;}return null;}
}

(七)用户实体类

用户实体类用于表示用户信息。

package com.example.entity;import lombok.Data;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;@Entity
@Data
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;private String password;
}

(八)用户仓库类

用户仓库类用于与数据库交互,获取用户信息。

package com.example.repository;import com.example.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;@Repository
public interface UserRepository extends JpaRepository<User, Long> {User findByUsername(String username);
}

四、Spring Boot 与 JWT 的高级应用

(一)JWT 刷新机制

在实际应用中,JWT 的有效期通常较短,例如 30 分钟。为了延长用户的会话时间,可以实现 JWT 刷新机制。当 JWT 过期时,客户端可以请求一个新的 JWT。

  1. 创建刷新令牌:在用户登录时,同时生成一个刷新令牌(Refresh Token),并将其存储在服务器端。
  2. 验证刷新令牌:当 JWT 过期时,客户端使用刷新令牌请求一个新的 JWT。
  3. 更新刷新令牌:每次生成新的 JWT 时,同时更新刷新令牌。

以下是一个简单的刷新令牌机制实现:

package com.example.controller;import com.example.entity.RefreshToken;
import com.example.service.RefreshTokenService;
import com.example.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/api/auth")
public class AuthController {@Autowiredprivate JwtUtil jwtUtil;@Autowiredprivate RefreshTokenService refreshTokenService;@PostMapping("/login")public ResponseEntity<?> login(@RequestBody User user) {// 用户认证逻辑User authenticatedUser = userService.authenticate(user.getUsername(), user.getPassword());if (authenticatedUser != null) {String token = jwtUtil.generateToken(authenticatedUser.getUsername());RefreshToken refreshToken = refreshTokenService.createRefreshToken(authenticatedUser.getId());return ResponseEntity.ok().body(new AuthResponse(token, refreshToken.getToken()));} else {return ResponseEntity.status(401).body("Invalid credentials");}}@PostMapping("/refresh")public ResponseEntity<?> refreshToken(@RequestBody RefreshTokenRequest refreshTokenRequest) {RefreshToken refreshToken = refreshTokenService.verifyRefreshToken(refreshTokenRequest.getRefreshToken());if (refreshToken != null) {String token = jwtUtil.generateToken(refreshToken.getUser().getUsername());return ResponseEntity.ok().body(new AuthResponse(token, refreshToken.getToken()));} else {return ResponseEntity.status(401).body("Invalid refresh token");}}
}

(二)JWT 黑名单机制

在某些情况下,可能需要提前使 JWT 失效,例如用户注销或修改密码。可以通过实现 JWT 黑名单机制来解决这个问题。当 JWT 被加入黑名单后,即使它未过期,也会被拒绝访问。

  1. 创建黑名单存储:可以使用内存数据库(如 Redis)存储黑名单中的 JWT。
  2. 验证 JWT:在拦截器中验证 JWT 时,检查它是否在黑名单中。

以下是一个简单的黑名单机制实现:

package com.example.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class BlacklistService {@Autowiredprivate RedisTemplate<String, String> redisTemplate;public void addToBlacklist(String token) {redisTemplate.opsForValue().set(token, "blacklisted", 3600, TimeUnit.SECONDS);}public boolean isTokenBlacklisted(String token) {return redisTemplate.hasKey(token);}
}

在拦截器中验证 JWT 是否在黑名单中:

package com.example.interceptor;import com.example.config.JwtConfig;
import com.example.service.BlacklistService;
import com.example.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
public class JwtInterceptor implements HandlerInterceptor {@Autowiredprivate JwtUtil jwtUtil;@Autowiredprivate BlacklistService blacklistService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {final String authHeader = request.getHeader(JwtConfig.HEADER);if (authHeader != null && authHeader.startsWith("Bearer ")) {final String token = authHeader.substring(7);if (jwtUtil.validateToken(token) && !blacklistService.isTokenBlacklisted(token)) {return true;}}response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");return false;}
}

(三)JWT 的安全性增强

虽然 JWT 本身是安全的,但在实际应用中,还需要考虑一些额外的安全措施,以防止潜在的安全威胁。

  1. 使用 HTTPS:确保 JWT 在传输过程中使用 HTTPS,防止中间人攻击。
  2. 限制 JWT 的使用范围:在 JWT 的 Payload 中添加 aud(受众)声明,限制 JWT 的使用范围。
  3. 使用非对称加密:在敏感应用中,可以使用非对称加密算法(如 RSA)生成 JWT,以提高安全性。
  4. 限制 JWT 的有效期:将 JWT 的有效期设置为较短的时间,例如 30 分钟,以减少 JWT 被滥用的风险。

五、Spring Boot 与 JWT 的最佳实践

(一)合理设置 JWT 的有效期

JWT 的有效期不宜过长或过短。过长的有效期会增加 JWT 被滥用的风险,而过短的有效期会频繁要求用户重新登录。建议将 JWT 的有效期设置为 30 分钟到 1 小时之间,并结合刷新令牌机制延长用户的会话时间。

(二)保护 JWT 的密钥

JWT 的密钥是安全的关键,必须妥善保护。建议将密钥存储在环境变量或配置中心中,避免直接写在代码中。同时,定期更换密钥,以减少密钥泄露的风险。

(三)使用 HTTPS

在生产环境中,必须使用 HTTPS 来保护 JWT 的传输安全。HTTPS 可以防止中间人攻击,确保 JWT 在客户端和服务器之间安全传输。

(四)限制 JWT 的使用范围

在 JWT 的 Payload 中添加 aud(受众)声明,限制 JWT 的使用范围。例如,可以指定 JWT 只能在特定的客户端或服务中使用,从而防止 JWT 被滥用。

(五)记录 JWT 的使用日志

记录 JWT 的生成、验证和失效日志,可以帮助开发者及时发现潜在的安全问题。例如,如果发现某个 JWT 被频繁验证,可能表明存在恶意攻击。

(六)实现 JWT 的黑名单机制

在某些情况下,可能需要提前使 JWT 失效,例如用户注销或修改密码。通过实现 JWT 的黑名单机制,可以将这些 JWT 加入黑名单,即使它们未过期,也会被拒绝访问。

六、Spring Boot 与 JWT 的常见问题与解决方案

(一)JWT 过期问题

当 JWT 过期时,客户端会收到 401 Unauthorized 错误。为了解决这个问题,可以实现 JWT 刷新机制,允许客户端使用刷新令牌请求一个新的 JWT。

(二)JWT 被篡改问题

JWT 的签名机制可以防止 JWT 被篡改。如果 JWT 被篡改,服务器在验证 JWT 时会抛出异常。为了解决这个问题,可以使用非对称加密算法(如 RSA)生成 JWT,以提高安全性。

(三)JWT 密钥泄露问题

如果 JWT 的密钥泄露,攻击者可以伪造 JWT。为了解决这个问题,必须妥善保护 JWT 的密钥,避免直接写在代码中。同时,定期更换密钥,以减少密钥泄露的风险。

(四)JWT 被滥用问题

如果 JWT 的有效期过长,可能会被滥用。为了解决这个问题,建议将 JWT 的有效期设置为较短的时间,并结合刷新令牌机制延长用户的会话时间。同时,限制 JWT 的使用范围,防止 JWT 被滥用。

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

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

相关文章

Code Runner MCP Server,来了!

大家好!我是韩老师。如果作为程序员的你,还不了解 MCP (Model Context Protocol) 的话,那韩老师劝你赶紧去补补课吧!本文不对 MCP 进行详细介绍~ 简单来说,MCP is another LSP in AI World! 也许有一天,AI 程序员不懂 MCP,就犹如前端程序员不懂 JavaScript!大家都知道韩…

Ollama初识

初识ollama,学习基本功能和记录参数前言 最近由于 deepseek 的火爆,AI 大语言模型又一次被抬了出来,对此早有关注的我决定尝试本地化部署使用体验一下,并且搭建一个本地的 AI 助手。 根据我之前了解到的信息,在使用大模型,现在较为方便的方法一般是先搭建一个大模型的管理…

Pass-15

function isImage($filename){//需要开启php_exif模块$image_type = exif_imagetype($filename);switch ($image_type) {case IMAGETYPE_GIF:return "gif";break;case IMAGETYPE_JPEG:return "jpg";break;case IMAGETYPE_PNG:return "png";break…

Pass-16

$is_upload = false; $msg = null; if (isset($_POST[submit])){// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径$filename = $_FILES[upload_file][name];$filetype = $_FILES[upload_file][type];$tmpname = $_FILES[upload_file][tmp_name];$target_path=UPLO…

upload pass-01

直接上传一句话木马貌似不行,尝试抓包看看发现全是get请求抓不到post传参上传一句话木马的包,合理猜测是不是前段验证,F12禁用JavaScript验证试试看可以看到成功上传并且返回了图片使用phpinfo()命令发现一句话木马成功上传 同理,是不是我们上传一个图片(此时通过了前段白…

# 集美大学课程实验报告-实验3:栈和队列

集美大学课程实验报告-实验2:线性表项目名称 内容课程名称 数据结构班级 网安2411指导教师 郑如滨学生姓名 李斌财学号 202421336021实验项目名称 站和队列上机实践日期上机实践时间 2学时一、目的(本次实验所涉及并要求掌握的知识点) 掌握STL中栈和队列的基本存储结构 掌握…

L3 设计,开发,认证

我都想笑了之十万八千个视频需要看。L3 设计,开发,认证 这里我暂时跳过了那些PPT里面要求看的视频,过一会再整理。 利益相关者和需求 ​ 从图中我们可以得到如下信息:航空器的生命周期:设计——生产——认证——运营 原始设备制造商(original equipment manufacturer,OE…

KMP 入门

前传:BF 算法 BF 算法即为暴力解法,一位一位向下匹配。 时间复杂度约为 \(O(n \times m)\)。KMP KMP 算法的主要思想是利用部分匹配信息,避免重复匹配,提高字符串查找效率。 KMP 算法总时间复杂度是 \(O(n + m)\),匹配用时 \(O(n)\)。 \(m\) 为模式串长度,\(n\) 为目标串…

web-CodeInject

<?php#Author: h1xaerror_reporting(0); show_source(__FILE__);eval("var_dump((Object)$_POST[1]);");eval("var_dump((Object)$_POST[1]);");:这行代码使用了eval()函数,这是一个非常危险的函数,因为它会执行传递给它的字符串作为PHP代码。这意味…

kettle插件-dm达梦数人大金仓Vastbase数据库插件

在国家大力倡导原创技术、推动信息技术应用创新(信创)的政策背景下,摆脱对国外技术的依赖、构建自主可控的信息技术体系成为重要发展方向。大数据作为信息技术的重要组成部分,国产大数据技术和产品迎来了前所未有的发展机遇。 信创旨在实现核心技术自主可控,保障国家信息安…

广义优势估计(GAE):端策略优化PPO中偏差与方差平衡的关键技术

广义优势估计(Generalized Advantage Estimation, GAE)由Schulman等人在2016年的论文中提出,是近端策略优化(PPO)算法的重要基础理论,也是促使PPO成为高效强化学习算法的核心因素之一。 GAE的理论基础建立在资格迹(eligibility traces)和时序差分λ(TD-λ)之上,为深入理解GA…