You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Security中实现无数据库的JWT无状态认证?

在Spring Security中实现无状态JWT认证的方案

要在Spring Security框架下实现JWT的无状态特性(避免每次认证查询数据库),核心是绕过默认的UserDetailsService依赖,直接通过JWT解析的信息完成认证流程。以下是具体实现步骤和代码示例:

核心思路

  1. 自定义JWT认证过滤器:在请求到达时直接解析JWT,提取用户身份、权限信息,构建Authentication对象并注入SecurityContext。
  2. 配置Spring Security:关闭session创建、移除默认的用户名密码认证过滤器,确保流程完全基于JWT的无状态逻辑。
  3. 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,提问作者이종원

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 19:12:47