Spring Boot整合Spring Security与JWT实现无状态认证:实战指南
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"
}
[*]登录获取Token
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企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]