在Thymeleaf与Spring中如何在请求头中传递JWT Token?
问题分析与解决方案
你当前的做法存在核心问题:在登录控制器中给响应设置Authorization头,浏览器不会自动将这个响应头的值携带到后续的请求中。再加上你配置了SessionCreationPolicy.STATELESS禁用会话,没有持久化的状态存储,所以必须换一种方式让前端请求自动带上JWT Token。
具体解决步骤
1. 调整登录控制器,将Token传递到前端页面
放弃给响应头设置Token,改为把Token放入模型中,传递到登录成功跳转的页面:
@PostMapping("/login") public ModelAndView processLogin(Authentication authentication) { // 替换为你实际的JWT生成逻辑,从authentication中提取用户信息生成Token String jwtToken = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkZXkiLCJpYXQiOjE2ODMxNzkxODMsImV4cCI6MTY4MzI2NTU4M30.3-_7VrfDg91TUKvHh18JIclt-NTmcx9UnZnsNMoCKyA"; // 跳转到dashboard页面,将Token传入模型 ModelAndView mv = new ModelAndView("dashboard"); mv.addObject("jwtToken", jwtToken); return mv; }
2. 在Thymeleaf页面中存储Token并配置全局请求拦截
在登录成功后的页面(如dashboard.html)添加JS代码,将Token存入本地存储,并让所有AJAX请求自动携带Authorization头:
<script th:inline="javascript"> // 从Thymeleaf模型中获取Token并存储到localStorage const token = [[${jwtToken}]]; if (token) { localStorage.setItem("jwtToken", token); } // 配置jQuery AJAX全局拦截器(使用jQuery时) $(document).ready(function() { $.ajaxSetup({ beforeSend: function(xhr) { const storedToken = localStorage.getItem("jwtToken"); if (storedToken) { xhr.setRequestHeader("Authorization", "Bearer " + storedToken); } } }); }); // 原生Fetch请求封装(不使用jQuery时) function fetchWithToken(url, options = {}) { const storedToken = localStorage.getItem("jwtToken"); options.headers = { ...options.headers, "Authorization": storedToken ? "Bearer " + storedToken : "" }; return fetch(url, options); } </script>
3. 修复Spring Security配置冲突
你当前同时启用了formLogin和SessionCreationPolicy.STATELESS,这两者存在冲突——formLogin默认依赖会话维持登录状态,而STATELESS会禁用会话。建议调整配置:
@Bean public SecurityFilterChain config(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeHttpRequests(auth -> auth .requestMatchers("/css/**","/scss/**","/vendor/**","/img/**","/js/**").permitAll() .requestMatchers("/auth","/register", "/login").permitAll() .anyRequest().authenticated() ) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) .authenticationProvider(authenticationProvider()) // 移除formLogin,改用自定义登录流程(避免与无状态会话冲突) .logout(logout -> logout .logoutSuccessUrl("/logout") .invalidateHttpSession(true) .deleteCookies("JSESSIONID") .permitAll() ) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); }
4. 处理非AJAX的页面跳转请求
如果是Thymeleaf的a标签整页跳转请求,浏览器不会自动携带Authorization头,可改用Cookie存储Token的方式:
- 在登录控制器中设置Cookie:
@PostMapping("/login") public ModelAndView processLogin(HttpServletResponse response) { String jwtToken = "你的Token"; Cookie cookie = new Cookie("jwtToken", jwtToken); cookie.setHttpOnly(true); // 防范XSS攻击 cookie.setSecure(true); // 生产环境开启,仅HTTPS下传递 cookie.setPath("/"); cookie.setMaxAge(86400); // 有效期1天 response.addCookie(cookie); ModelAndView mv = new ModelAndView("dashboard"); return mv; }
- 修改
JwtAuthFilter从Cookie中获取Token:
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String jwtToken = null; Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if ("jwtToken".equals(cookie.getName())) { jwtToken = cookie.getValue(); break; } } } // 后续Token验证逻辑... filterChain.doFilter(request, response); }
内容的提问来源于stack exchange,提问作者maudderung
相关产品推荐
相关产品推荐

