Spring Boot集成JWT:打造安全的RESTful API

news/2025/3/20 6:14:30/

Spring Boot集成JWT:打造安全的RESTful API


1. JWT简介

JWT(JSON Web Token) 是一种开放标准(RFC 7519),用于在各方之间安全传输信息。它广泛应用于身份认证和授权场景,尤其适合无状态的RESTful API开发。

JWT的组成

  1. Header:声明令牌类型(如JWT)和签名算法(如HS256)。
    {"alg": "HS256","typ": "JWT"
    }
    
  2. Payload:存储用户信息(如用户ID、角色)和其他元数据(如过期时间)。
    {"sub": "admin","iat": 1625145600,"exp": 1625232000
    }
    
  3. Signature:通过密钥对Header和Payload进行加密生成的签名,确保数据未被篡改。

JWT的优势

  • 无状态:服务端无需存储会话信息,减轻服务器压力。
  • 跨语言支持:几乎所有编程语言都有JWT实现库。
  • 易于扩展:Payload可灵活添加自定义字段。

2. 环境准备

步骤1:创建Spring Boot项目
使用 Spring Initializr 生成项目,勾选以下依赖:

  • Spring Web
  • Spring Security

步骤2:添加JJWT依赖(Maven)

<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>

步骤3:配置文件

# application.properties
# JWT配置
jwt.secret=MySuperSecretKey123!@#  # 密钥(需强密码)
jwt.expiration=86400000            # 有效期24小时(单位:毫秒)

3. 核心实现

3.1 JWT工具类

@Component
public class JwtUtils {@Value("${jwt.secret}")private String secretKey;@Value("${jwt.expiration}")private long expiration;// 生成令牌public String generateToken(String username) {return Jwts.builder().setSubject(username).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + expiration)).signWith(SignatureAlgorithm.HS256, secretKey).compact();}// 验证令牌public boolean validateToken(String token) {try {Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);return true;} catch (JwtException | IllegalArgumentException e) {return false;}}// 解析用户名public String getUsernameFromToken(String token) {return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();}
}

3.2 Spring Security配置

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {private final JwtUtils jwtUtils;public SecurityConfig(JwtUtils jwtUtils) {this.jwtUtils = jwtUtils;}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers("/api/login").permitAll().anyRequest().authenticated().and().addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class);}// JWT验证过滤器private Filter jwtFilter() {return new OncePerRequestFilter() {@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain) throws ServletException, IOException {String header = request.getHeader("Authorization");if (header != null && header.startsWith("Bearer ")) {String token = header.substring(7);if (jwtUtils.validateToken(token)) {String username = jwtUtils.getUsernameFromToken(token);Authentication auth = new UsernamePasswordAuthenticationToken(username, null, Collections.emptyList());SecurityContextHolder.getContext().setAuthentication(auth);}}filterChain.doFilter(request, response);}};}
}

3.3 登录接口

@RestController
@RequestMapping("/api")
public class AuthController {@Autowiredprivate JwtUtils jwtUtils;@PostMapping("/login")public ResponseEntity<?> login(@RequestBody LoginRequest request) {// 实际项目中应查询数据库验证用户if ("admin".equals(request.getUsername()) && "admin123".equals(request.getPassword())) {String token = jwtUtils.generateToken(request.getUsername());return ResponseEntity.ok(new JwtResponse(token));}return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();}
}@Data
class LoginRequest {private String username;private String password;
}@Data
class JwtResponse {private String token;public JwtResponse(String token) {this.token = token;}
}

4. 高级配置

4.1 自定义异常处理

@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(value = {ExpiredJwtException.class,MalformedJwtException.class,SignatureException.class})public ResponseEntity<?> handleJwtException(Exception ex) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("error", ex.getMessage()));}
}

4.2 基于角色的权限控制

@GetMapping("/admin/dashboard")
@PreAuthorize("hasRole('ADMIN')")
public String adminDashboard() {return "Admin Access Granted";
}

4.3 跨域配置

@Bean
public CorsFilter corsFilter() {UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();CorsConfiguration config = new CorsConfiguration();config.addAllowedOrigin("*");config.addAllowedHeader("*");config.addAllowedMethod("*");source.registerCorsConfiguration("/**", config);return new CorsFilter(source);
}

5. 常见问题与解决方案

问题1:令牌解析失败

  • 可能原因
    • 密钥不匹配
    • 令牌过期
    • 令牌被篡改
  • 排查步骤
    1. 检查jwt.secret配置是否一致
    2. 使用 jwt.io 调试令牌

问题2:令牌刷新机制

  • 实现方案
    1. 客户端在令牌过期前调用/api/refresh接口
    2. 服务端验证旧令牌有效性后生成新令牌

问题3:Spring Security兼容性

  • 典型错误Cannot convert access token to JSON
  • 解决方案:确保依赖版本一致,检查jjwt-impl是否包含在运行时依赖中

6. 总结与扩展

应用场景

  • 微服务间的安全通信
  • 移动应用用户认证
  • 前后端分离架构的权限管理

扩展学习

  • JWT官方文档
  • Spring Security官方指南
  • 实战案例:GitHub电商项目集成JWT

流程图:JWT认证流程

Client Server Database POST /api/login (username/password) 验证用户凭证 返回用户数据 生成JWT 返回JWT令牌 GET /protected (Header: Bearer <token>) 验证JWT签名和有效期 返回受保护资源 Client Server Database

避坑指南

  1. 密钥安全:切勿硬编码密钥,应通过环境变量注入
  2. 令牌存储:客户端使用HttpOnly Cookie或SecureLocalStorage
  3. 时间同步:确保服务器间时钟同步,避免令牌有效期计算错误

通过本指南,您已掌握在Spring Boot中集成JWT的核心技术。立即动手实践,为您的API穿上安全盔甲! 🔐


http://www.ppmy.cn/news/1580523.html

相关文章

华为云在工业数字化方面的优势

华为云在工业数字化领域展现出全方位的优势&#xff0c;为制造业的转型升级提供了强大的助力。 一、专业的数字化诊断治理服务 华为云的数字化诊断治理专家服务为企业提供全面的深度诊断、成熟度评估、产业升级分析、创新治理和专家咨询等服务。其诊断模型参考国际国内标准&a…

系统思考:恶性循环

去年&#xff0c;我给一家知名人力资源公司交付了两个项目——一个在6月&#xff0c;另一个在8月&#xff0c;至今半年多了依然没有收到课酬。催促多次&#xff0c;得到的答复却各式各样&#xff1a;销售说老板卡了额度&#xff0c;老板说具体情况还需了解。每一次的推诿&#…

[贪心算法]-最大数(lambda 表达式的补充)

1.解析 我们一般使用的排序比较大小都是 a>b 那么a在b的前面 ab 无所谓 a<b a在b的后面 本题的排序则是 ab>ba 那么a在b的前面 abba 无所谓 ab<ba a在b的后面 2.代码 class Solution { public:string largestNumber(vector<int>& nums) {//1.先把所有…

Deepseek本地部署指南:在linux服务器部署,在mac远程web-ui访问

DeepSeek本地部署指南&#xff0c;特别是在Linux服务器上部署并在Mac上实现远程Web-UI访问&#xff0c;可以按照以下步骤进行&#xff1a; 一、在Linux服务器上部署DeepSeek模型 安装Ollama 使用命令curl -sSfL https://ollama.com/install.sh | sh来安装Ollama。安装完成后&a…

利用knn算法实现手写数字分类

利用knn算法实现手写数字分类 1.作者介绍2.KNN算法2.1KNN&#xff08;K-Nearest Neighbors&#xff09;算法核心思想2.2KNN算法的工作流程2.3优缺点2.4 KNN算法图示介绍 3.实验过程3.1安装所需库3.2 MNIST数据集3.3 导入手写数字图像进行分类3.4 完整代码3.5 实验结果 1.作者介…

处理变长的时间序列

pytorch中torch.nn.utils.rnn相关sequence的pad和pack操作 官网…torch.nn.utils.rnn.pack_padded_sequence 知乎pack_padded_sequence 和 pad_packed_sequence 结论 ✅ pack_padded_sequence 是最好的方法&#xff08;避免无效计算&#xff0c;提升性能&#xff09; &#x…

C语言:编程设计猜数游戏

先由计算机想一个数给用户猜&#xff0c;如果猜对了&#xff0c;提示“right&#xff01;”&#xff0c;猜错了&#xff0c;提示“wrong&#xff01;及大小” 思路&#xff1a;用随机函数rand&#xff08;&#xff09;取到计算机想的数 代码&#xff1a; #include <stdio.…

Linux 命令学习记录

Linux 命令详解与进阶指南 Linux 是一种广泛使用的开源操作系统&#xff0c;掌握 Linux 命令是开发者和系统管理员的必备技能。本文将详细介绍 Linux 的常用命令&#xff0c;并涵盖一些高级进阶技巧&#xff0c;帮助你更高效地使用 Linux。 目录 基础命令 文件与目录操作文本…