如何在Spring Security中实现无数据库的JWT无状态认证?
在Spring Security中实现无状态JWT认证的方案
要在Spring Security框架下实现JWT的无状态特性(避免每次认证查询数据库),核心是绕过默认的UserDetailsService依赖,直接通过JWT解析的信息完成认证流程。以下是具体实现步骤和代码示例:
核心思路
- 自定义JWT认证过滤器:在请求到达时直接解析JWT,提取用户身份、权限信息,构建
Authentication对象并注入SecurityContext。 - 配置Spring Security:关闭session创建、移除默认的用户名密码认证过滤器,确保流程完全基于JWT的无状态逻辑。
- JWT预存必要信息:生成JWT时将用户名、权限列表等核心数据写入token,避免后续查库。
1. 自定义JWT认证过滤器
这个过滤器会优先于默认认证逻辑执行,直接从请求头解析JWT并完成认证:
@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtTokenProvider jwtTokenProvider; public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) { this.jwtTokenProvider = jwtTokenProvider; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = extractTokenFromRequest(request); if (token != null && jwtTokenProvider.isTokenValid(token)) { // 从JWT解析用户核心信息 String username = jwtTokenProvider.getUsername(token); List<String> userRoles = jwtTokenProvider.getRoles(token); // 构建Spring Security认可的权限集合 Collection<GrantedAuthority> authorities = userRoles.stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); // 生成Authentication对象(无需密码,因为JWT已完成身份验证) Authentication authentication = new UsernamePasswordAuthenticationToken( username, null, authorities ); // 将认证信息注入SecurityContext,后续Spring Security会直接使用该信息 SecurityContextHolder.getContext().setAuthentication(authentication); } filterChain.doFilter(request, response); } private String extractTokenFromRequest(HttpServletRequest request) { String authHeader = request.getHeader("Authorization"); if (StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) { return authHeader.substring(7); } return null; } }
2. JWT工具类(生成/解析token)
负责JWT的生成、解析和有效性校验,确保token包含用户权限信息:
@Component public class JwtTokenProvider { // 建议从配置文件读取密钥和过期时间 private final String secretKey = "your-strong-secret-key"; private final long tokenExpirationMs = 3600000; // 1小时 @PostConstruct protected void init() { // 对密钥进行Base64编码增强安全性 secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes()); } // 生成JWT(登录成功时调用) public String generateToken(String username, List<String> roles) { Claims claims = Jwts.claims().setSubject(username); claims.put("roles", roles); Date now = new Date(); Date expirationDate = new Date(now.getTime() + tokenExpirationMs); return Jwts.builder() .setClaims(claims) .setIssuedAt(now) .setExpiration(expirationDate) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); } // 从token提取用户名 public String getUsername(String token) { return Jwts.parser().setSigningKey(secretKey) .parseClaimsJws(token).getBody().getSubject(); } // 从token提取用户角色 public List<String> getRoles(String token) { return Jwts.parser().setSigningKey(secretKey) .parseClaimsJws(token).getBody().get("roles", List.class); } // 校验token有效性(签名、过期时间) public boolean isTokenValid(String token) { try { Jws<Claims> claimsJws = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token); return !claimsJws.getBody().getExpiration().before(new Date()); } catch (JwtException | IllegalArgumentException e) { throw new RuntimeException("无效或过期的JWT Token"); } } }
3. Spring Security配置类
关闭session、移除默认认证过滤器,确保流程完全无状态:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private final JwtAuthenticationFilter jwtAuthenticationFilter; public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) { this.jwtAuthenticationFilter = jwtAuthenticationFilter; } @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() // JWT不需要CSRF保护 .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 核心:禁用session,实现无状态 .and() .authorizeRequests() .antMatchers("/auth/login", "/auth/register").permitAll() // 放行登录注册接口 .anyRequest().authenticated() .and() // 将自定义JWT过滤器放到默认用户名密码过滤器之前 .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); // 移除默认的用户名密码认证过滤器,避免触发查库逻辑 http.removeFilter(UsernamePasswordAuthenticationFilter.class); } // 无需配置UserDetailsService,因为我们不走默认认证流程 @Override public void configure(AuthenticationManagerBuilder auth) throws Exception {} }
关键注意事项
- SessionCreationPolicy.STATELESS:必须设置该属性,告诉Spring Security不创建、不依赖session,完全基于JWT的认证信息处理请求。
- JWT内容完整性:生成token时必须包含用户权限信息,否则后续授权逻辑无法正常工作。
- JWT失效处理:由于JWT本身是无状态的,无法主动注销,若需实现注销功能,可通过Redis维护JWT黑名单,在过滤器中先校验token是否在黑名单内。
内容的提问来源于stack exchange,提问作者이종원
相关产品推荐
相关产品推荐

