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

Spring Security无状态模式下,能否在Thymeleaf中使用Authentication对象?

在Spring Security无状态会话下在Thymeleaf中使用Authentication对象

完全可以做到,不过得先搞明白为啥默认情况下拿不到——这和SessionCreationPolicy.STATELESS的特性直接挂钩:

当你把会话策略设为STATELESS时,Spring Security不会把Authentication对象存到HttpSession里,而且每个请求都是独立的,不会保留任何会话信息。Thymeleaf的Spring Security方言里的#authentication本质上是从SecurityContextHolder.getContext().getAuthentication()取对象,但问题出在:

  • 登录请求成功后,Authentication只存在于当前请求的SecurityContextHolder里,跳转后的新请求(比如你跳去/main)如果没带JWT令牌,服务器就不会把Authentication放到SecurityContextHolder中,Thymeleaf自然拿不到。
  • 要是改回非无状态策略,Spring Security会把Authentication存入HttpSession,后续请求从Session里读,所以#authentication就能正常显示了。

下面给你两种可行的解决方案:


方案1:登录成功时手动传递Authentication到跳转页面

如果你只是在登录后的跳转页面需要显示Authentication,可以在登录成功处理器里把Authentication放到Flash属性中,这样跳转后页面能一次性获取到:

首先修改你的successHandler()方法:

private AuthenticationSuccessHandler successHandler() {
    return (request, response, authentication) -> {
        // 获取RedirectAttributes来存储Flash属性
        RedirectAttributes redirectAttributes = (RedirectAttributes) request.getAttribute(
            "org.springframework.web.servlet.mvc.support.RedirectAttributesModel");
        if (redirectAttributes != null) {
            // 把Authentication对象存入Flash属性,跳转后页面可读取
            redirectAttributes.addFlashAttribute("currentAuth", authentication);
        }
        // 跳转到目标页面
        response.sendRedirect("/main");
    };
}

然后在Thymeleaf页面里直接使用这个Flash属性:

<h1>[[${currentAuth}]]</h1>

Flash属性的特点是只会在跳转后的第一次请求中有效,适合登录后跳转的场景。


方案2:配置JWT过滤器,让所有请求自动填充SecurityContext

这是更符合无状态JWT认证的标准做法:通过自定义过滤器,每次请求时从请求头(或参数)中提取JWT令牌,验证后将Authentication存入SecurityContextHolder,这样Thymeleaf的#authentication就能正常工作了。

步骤1:创建JWT认证过滤器

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    // 注入自定义的JWT工具类,用于解析和验证令牌
    private final JwtTokenUtil jwtTokenUtil;

    public JwtAuthenticationFilter(JwtTokenUtil jwtTokenUtil) {
        this.jwtTokenUtil = jwtTokenUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 从请求头中提取JWT令牌(格式通常为Authorization: Bearer xxx)
        String token = extractTokenFromHeader(request);
        
        if (token != null && jwtTokenUtil.isTokenValid(token)) {
            // 从令牌中解析用户信息,构建Authentication对象
            Authentication authentication = jwtTokenUtil.getAuthenticationFromToken(token);
            // 将Authentication存入SecurityContextHolder
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        
        // 继续执行后续过滤器链
        filterChain.doFilter(request, response);
    }

    private String extractTokenFromHeader(HttpServletRequest request) {
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            return authHeader.substring(7);
        }
        return null;
    }
}

步骤2:在Security配置中添加这个过滤器

修改你的SecurityConfiguration:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    MyUserDetailsService myUserDetailsService;
    @Autowired
    JwtTokenUtil jwtTokenUtil;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/index", "/main", "/login").permitAll()
                .anyRequest().authenticated();
        
        http.formLogin()
                .loginPage("/login")
                .permitAll()
                .successHandler(successHandler());
        
        // 添加JWT过滤器,放在UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(new JwtAuthenticationFilter(jwtTokenUtil), UsernamePasswordAuthenticationFilter.class);
        
        http.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    // 你的successHandler实现,需要在登录成功时生成并返回JWT令牌
    private AuthenticationSuccessHandler successHandler() {
        return (request, response, authentication) -> {
            String token = jwtTokenUtil.generateToken(authentication);
            // 将令牌放入响应头,让前端后续请求携带
            response.setHeader("Authorization", "Bearer " + token);
            response.sendRedirect("/main");
        };
    }
}

这样,后续所有请求只要携带有效的JWT令牌,过滤器就会自动把Authentication存入SecurityContextHolder,Thymeleaf里的[[${#authentication}]]就能正常显示了。


总结一下:如果只是登录跳转页面临时显示,用方案1简单快捷;如果是所有需要权限的页面都要显示Authentication,方案2是更规范的无状态认证做法。

内容的提问来源于stack exchange,提问作者Yong D

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 15:52:53