Spring Boot整合Spring Security与JWT实现无状态认证:实战指南 ...

打印 上一主题 下一主题

主题 1033|帖子 1033|积分 3099

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
Spring Boot整合Spring Security与JWT实现无状态认证:实战指南

一、JWT认证原理简介

JSON Web Token(JWT)是一种开放标准(RFC 7519),由三部分组成:


  • Header(头部):声明令牌范例和签名算法
  • Payload(负载):携带用户身份信息
  • Signature(签名):防窜改验证
认证流程:

  • 客户端提交登录凭证
  • 服务端验证通过后天生JWT
  • 客户端后续哀求携带JWT
  • 服务端验证JWT有用性
二、项目搭建与依赖准备

1. 创建Spring Boot项目

选择依赖:


  • Spring Web
  • Spring Security
  • Lombok
  • Spring Data JPA(数据库存储)
2. 添加JWT依赖

  1. <dependency>
  2.     <groupId>io.jsonwebtoken</groupId>
  3.     <artifactId>jjwt-api</artifactId>
  4.     <version>0.11.5</version>
  5. </dependency>
  6. <dependency>
  7.     <groupId>io.jsonwebtoken</groupId>
  8.     <artifactId>jjwt-impl</artifactId>
  9.     <version>0.11.5</version>
  10.     <scope>runtime</scope>
  11. </dependency>
  12. <dependency>
  13.     <groupId>io.jsonwebtoken</groupId>
  14.     <artifactId>jjwt-jackson</artifactId>
  15.     <version>0.11.5</version>
  16.     <scope>runtime</scope>
  17. </dependency>
复制代码
三、核心组件实现

1. JWT工具类

  1. public class JwtUtils {
  2.     private static final String SECRET_KEY = "your-256-bit-secret";
  3.     private static final long EXPIRATION = 86400000; // 24小时
  4.     public static String generateToken(UserDetails userDetails) {
  5.         return Jwts.builder()
  6.                 .setSubject(userDetails.getUsername())
  7.                 .claim("roles", userDetails.getAuthorities())
  8.                 .setIssuedAt(new Date())
  9.                 .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
  10.                 .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
  11.                 .compact();
  12.     }
  13.     public static Claims parseToken(String token) {
  14.         return Jwts.parser()
  15.                 .setSigningKey(SECRET_KEY)
  16.                 .parseClaimsJws(token)
  17.                 .getBody();
  18.     }
  19.     public static boolean validateToken(String token, UserDetails userDetails) {
  20.         final String username = extractUsername(token);
  21.         return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
  22.     }
  23.     private static boolean isTokenExpired(String token) {
  24.         return extractExpiration(token).before(new Date());
  25.     }
  26.     // 其他辅助方法...
  27. }
复制代码
2. 自定义UserDetails实现

  1. @Entity
  2. @Data
  3. public class User implements UserDetails {
  4.     @Id
  5.     @GeneratedValue(strategy = GenerationType.IDENTITY)
  6.     private Long id;
  7.     private String username;
  8.     private String password;
  9.    
  10.     @ManyToMany(fetch = FetchType.EAGER)
  11.     private Set<Role> roles;
  12.     @Override
  13.     public Collection<? extends GrantedAuthority> getAuthorities() {
  14.         return roles.stream()
  15.                 .map(role -> new SimpleGrantedAuthority(role.getName()))
  16.                 .collect(Collectors.toList());
  17.     }
  18.     // 实现其他UserDetails方法...
  19. }
复制代码
四、安全配置类

1. Spring Security配置

  1. @Configuration
  2. @EnableWebSecurity
  3. @RequiredArgsConstructor
  4. public class SecurityConfig {
  5.    
  6.     private final JwtAuthenticationFilter jwtAuthFilter;
  7.    
  8.     @Bean
  9.     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  10.         http
  11.             .csrf(AbstractHttpConfigurer::disable)
  12.             .authorizeHttpRequests(auth -> auth
  13.                 .requestMatchers("/api/auth/**").permitAll()
  14.                 .anyRequest().authenticated()
  15.             )
  16.             .sessionManagement(session -> session
  17.                 .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  18.             )
  19.             .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
  20.         
  21.         return http.build();
  22.     }
  23.     @Bean
  24.     public PasswordEncoder passwordEncoder() {
  25.         return new BCryptPasswordEncoder();
  26.     }
  27. }
复制代码
2. JWT认证过滤器

  1. @Component
  2. @RequiredArgsConstructor
  3. public class JwtAuthenticationFilter extends OncePerRequestFilter {
  4.     private final JwtUtils jwtUtils;
  5.     private final UserDetailsService userDetailsService;
  6.     @Override
  7.     protected void doFilterInternal(HttpServletRequest request,
  8.                                     HttpServletResponse response,
  9.                                     FilterChain filterChain) throws ServletException, IOException {
  10.         final String authHeader = request.getHeader("Authorization");
  11.         if (authHeader == null || !authHeader.startsWith("Bearer ")) {
  12.             filterChain.doFilter(request, response);
  13.             return;
  14.         }
  15.         try {
  16.             final String jwt = authHeader.substring(7);
  17.             final String username = jwtUtils.extractUsername(jwt);
  18.             
  19.             if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
  20.                 UserDetails userDetails = userDetailsService.loadUserByUsername(username);
  21.                 if (jwtUtils.validateToken(jwt, userDetails)) {
  22.                     UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
  23.                             userDetails, null, userDetails.getAuthorities());
  24.                     authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
  25.                     SecurityContextHolder.getContext().setAuthentication(authToken);
  26.                 }
  27.             }
  28.         } catch (Exception e) {
  29.             logger.error("Cannot set user authentication", e);
  30.         }
  31.         
  32.         filterChain.doFilter(request, response);
  33.     }
  34. }
复制代码
五、认证控制器实现

  1. @RestController
  2. @RequestMapping("/api/auth")
  3. @RequiredArgsConstructor
  4. public class AuthController {
  5.     private final AuthenticationManager authenticationManager;
  6.     private final JwtUtils jwtUtils;
  7.     private final UserService userService;
  8.     @PostMapping("/login")
  9.     public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest request) {
  10.         Authentication authentication = authenticationManager.authenticate(
  11.                 new UsernamePasswordAuthenticationToken(
  12.                         request.getUsername(),
  13.                         request.getPassword()));
  14.         
  15.         SecurityContextHolder.getContext().setAuthentication(authentication);
  16.         UserDetails userDetails = (UserDetails) authentication.getPrincipal();
  17.         
  18.         String jwt = jwtUtils.generateToken(userDetails);
  19.         return ResponseEntity.ok(new JwtResponse(jwt));
  20.     }
  21.     @PostMapping("/register")
  22.     public ResponseEntity<?> registerUser(@RequestBody RegisterRequest request) {
  23.         if (userService.existsByUsername(request.getUsername())) {
  24.             return ResponseEntity.badRequest().body("用户名已存在");
  25.         }
  26.         
  27.         User user = new User();
  28.         user.setUsername(request.getUsername());
  29.         user.setPassword(passwordEncoder.encode(request.getPassword()));
  30.         userService.saveUser(user);
  31.         
  32.         return ResponseEntity.ok("用户注册成功");
  33.     }
  34. }
复制代码
六、测试验证

使用Postman测试流程:

  • 注册用户
  1. POST /api/auth/register
  2. {
  3.     "username": "testuser",
  4.     "password": "Test@1234"
  5. }
复制代码

  • 登录获取Token
  1. POST /api/auth/login
  2. {
  3.     "username": "testuser",
  4.     "password": "Test@1234"
  5. }
  6. 响应:
  7. {
  8.     "token": "eyJhbGciOiJIUzI1NiJ9.xxxxxx"
  9. }
复制代码

  • 访问受保护接口
  1. GET /api/protected
  2. Headers: Authorization: Bearer <your-token>
复制代码
七、安全加强措施


  • 密钥管理:
  1. // 推荐从环境变量读取密钥
  2. private static final String SECRET_KEY = System.getenv("JWT_SECRET");
复制代码

  • 革新令牌机制:
  1. @PostMapping("/refresh-token")
  2. public ResponseEntity<?> refreshToken(HttpServletRequest request) {
  3.     String refreshToken = jwtUtils.getRefreshTokenFromRequest(request);
  4.     // 验证刷新令牌并颁发新访问令牌
  5. }
复制代码

  • 黑名单机制:
  1. // 登出时将令牌加入黑名单
  2. public void logout(String token) {
  3.     long expiration = jwtUtils.getExpirationFromToken(token);
  4.     long currentTime = System.currentTimeMillis();
  5.     if (expiration > currentTime) {
  6.         blacklistService.addToBlacklist(token, expiration - currentTime);
  7.     }
  8. }
复制代码
八、最佳实践建议


  • 使用HTTPS传输JWT
  • 设置公道的令牌有用期(访问令牌1小时,革新令牌7天)
  • 存储敏感信息在服务端,Payload只放必要信息
  • 定期轮换签名密钥
  • 实现令牌吊销机制
  • 监控非常认证尝试
  • 使用强密码计谋(至少8位,包罗大小写字母、数字和特殊字符)
结语

通过本实践案例,我们实现了基于JWT的无状态认证系统。实际开辟中还必要考虑:


  • 分布式系统的会话管理
  • 微服务架构中的令牌传递
  • 第三方登录集成(OAuth2)
  • 审计日志记录
建议联合具体业务需求调整安全计谋,并定期进行安全渗透测试。Spring Security与JWT的联合为现代Web应用提供了灵活强盛的安全办理方案,正确实行可以有用保护系统资源。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

卖不甜枣

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表