马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
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依赖
- <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>
复制代码 三、核心组件实现
1. JWT工具类
- public class JwtUtils {
- private static final String SECRET_KEY = "your-256-bit-secret";
- private static final long EXPIRATION = 86400000; // 24小时
- public static String generateToken(UserDetails userDetails) {
- return Jwts.builder()
- .setSubject(userDetails.getUsername())
- .claim("roles", userDetails.getAuthorities())
- .setIssuedAt(new Date())
- .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
- .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
- .compact();
- }
- public static Claims parseToken(String token) {
- return Jwts.parser()
- .setSigningKey(SECRET_KEY)
- .parseClaimsJws(token)
- .getBody();
- }
- public static boolean validateToken(String token, UserDetails userDetails) {
- final String username = extractUsername(token);
- return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
- }
- private static boolean isTokenExpired(String token) {
- return extractExpiration(token).before(new Date());
- }
- // 其他辅助方法...
- }
复制代码 2. 自定义UserDetails实现
- @Entity
- @Data
- public class User implements UserDetails {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
- private String username;
- private String password;
-
- @ManyToMany(fetch = FetchType.EAGER)
- private Set<Role> roles;
- @Override
- public Collection<? extends GrantedAuthority> getAuthorities() {
- return roles.stream()
- .map(role -> new SimpleGrantedAuthority(role.getName()))
- .collect(Collectors.toList());
- }
- // 实现其他UserDetails方法...
- }
复制代码 四、安全配置类
1. Spring Security配置
- @Configuration
- @EnableWebSecurity
- @RequiredArgsConstructor
- public class SecurityConfig {
-
- private final JwtAuthenticationFilter jwtAuthFilter;
-
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- .csrf(AbstractHttpConfigurer::disable)
- .authorizeHttpRequests(auth -> auth
- .requestMatchers("/api/auth/**").permitAll()
- .anyRequest().authenticated()
- )
- .sessionManagement(session -> session
- .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
- )
- .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
-
- return http.build();
- }
- @Bean
- public PasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
- }
- }
复制代码 2. JWT认证过滤器
- @Component
- @RequiredArgsConstructor
- public class JwtAuthenticationFilter extends OncePerRequestFilter {
- private final JwtUtils jwtUtils;
- private final UserDetailsService userDetailsService;
- @Override
- protected void doFilterInternal(HttpServletRequest request,
- HttpServletResponse response,
- FilterChain filterChain) throws ServletException, IOException {
- final String authHeader = request.getHeader("Authorization");
- if (authHeader == null || !authHeader.startsWith("Bearer ")) {
- filterChain.doFilter(request, response);
- return;
- }
- try {
- final String jwt = authHeader.substring(7);
- final String username = jwtUtils.extractUsername(jwt);
-
- if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
- UserDetails userDetails = userDetailsService.loadUserByUsername(username);
- if (jwtUtils.validateToken(jwt, userDetails)) {
- UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
- userDetails, null, userDetails.getAuthorities());
- authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
- SecurityContextHolder.getContext().setAuthentication(authToken);
- }
- }
- } catch (Exception e) {
- logger.error("Cannot set user authentication", e);
- }
-
- filterChain.doFilter(request, response);
- }
- }
复制代码 五、认证控制器实现
- @RestController
- @RequestMapping("/api/auth")
- @RequiredArgsConstructor
- public class AuthController {
- private final AuthenticationManager authenticationManager;
- private final JwtUtils jwtUtils;
- private final UserService userService;
- @PostMapping("/login")
- public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest request) {
- Authentication authentication = authenticationManager.authenticate(
- new UsernamePasswordAuthenticationToken(
- request.getUsername(),
- request.getPassword()));
-
- SecurityContextHolder.getContext().setAuthentication(authentication);
- UserDetails userDetails = (UserDetails) authentication.getPrincipal();
-
- String jwt = jwtUtils.generateToken(userDetails);
- return ResponseEntity.ok(new JwtResponse(jwt));
- }
- @PostMapping("/register")
- public ResponseEntity<?> registerUser(@RequestBody RegisterRequest request) {
- if (userService.existsByUsername(request.getUsername())) {
- return ResponseEntity.badRequest().body("用户名已存在");
- }
-
- User user = new User();
- user.setUsername(request.getUsername());
- user.setPassword(passwordEncoder.encode(request.getPassword()));
- userService.saveUser(user);
-
- return ResponseEntity.ok("用户注册成功");
- }
- }
复制代码 六、测试验证
使用Postman测试流程:
- POST /api/auth/register
- {
- "username": "testuser",
- "password": "Test@1234"
- }
复制代码- POST /api/auth/login
- {
- "username": "testuser",
- "password": "Test@1234"
- }
- 响应:
- {
- "token": "eyJhbGciOiJIUzI1NiJ9.xxxxxx"
- }
复制代码- GET /api/protected
- Headers: Authorization: Bearer <your-token>
复制代码 七、安全加强措施
- // 推荐从环境变量读取密钥
- private static final String SECRET_KEY = System.getenv("JWT_SECRET");
复制代码- @PostMapping("/refresh-token")
- public ResponseEntity<?> refreshToken(HttpServletRequest request) {
- String refreshToken = jwtUtils.getRefreshTokenFromRequest(request);
- // 验证刷新令牌并颁发新访问令牌
- }
复制代码- // 登出时将令牌加入黑名单
- public void logout(String token) {
- long expiration = jwtUtils.getExpirationFromToken(token);
- long currentTime = System.currentTimeMillis();
- if (expiration > currentTime) {
- blacklistService.addToBlacklist(token, expiration - currentTime);
- }
- }
复制代码 八、最佳实践建议
- 使用HTTPS传输JWT
- 设置公道的令牌有用期(访问令牌1小时,革新令牌7天)
- 存储敏感信息在服务端,Payload只放必要信息
- 定期轮换签名密钥
- 实现令牌吊销机制
- 监控非常认证尝试
- 使用强密码计谋(至少8位,包罗大小写字母、数字和特殊字符)
结语
通过本实践案例,我们实现了基于JWT的无状态认证系统。实际开辟中还必要考虑:
- 分布式系统的会话管理
- 微服务架构中的令牌传递
- 第三方登录集成(OAuth2)
- 审计日志记录
建议联合具体业务需求调整安全计谋,并定期进行安全渗透测试。Spring Security与JWT的联合为现代Web应用提供了灵活强盛的安全办理方案,正确实行可以有用保护系统资源。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |