SpringBoot与JS通信:重定向时如何携带Authorization Header?
问题核心
你当前的代码逻辑存在关键误区:用axios.get(redirect)带请求头发起请求后,又执行window.location.href = redirect,这两个是完全独立的请求。前者是AJAX请求(带自定义头),后者是浏览器发起的全新页面跳转请求(不会继承AJAX的请求头),所以跳转后的请求自然没有Authorization头。且浏览器的安全限制不允许JS直接修改页面跳转请求的HTTP头,这种方式根本无法实现需求。
可行实现方式
方式一:用Cookie存储JWT(推荐,适配服务端渲染场景)
服务端渲染(Thymeleaf)最适合用Cookie传递凭证,因为Cookie会自动随所有同域请求发送到服务器,无需前端手动处理:
- 后端修改:登录接口验证成功后,将JWT写入HttpOnly、Secure的Cookie中(提升安全性):
// 登录成功后设置Cookie Cookie jwtCookie = new Cookie("jwtToken", jwt); jwtCookie.setHttpOnly(true); // 防止XSS窃取 jwtCookie.setSecure(true); // 仅HTTPS下传输,生产环境开启 jwtCookie.setPath("/"); // 全站有效 jwtCookie.setMaxAge(7 * 24 * 60 * 60); // 有效期7天 response.addCookie(jwtCookie); // 直接返回重定向响应,由浏览器跳转 return "redirect:/dashboard";
- 修改JWT过滤器:让过滤器同时支持从Authorization头和Cookie中提取Token:
public class JwtFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = null; // 先从Authorization头取 String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { token = authHeader.substring(7); } // 头里没有就从Cookie取 else { Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if ("jwtToken".equals(cookie.getName())) { token = cookie.getValue(); break; } } } } // 后续的Token验证逻辑... if (token != null && jwtUtil.validateToken(token)) { // 设置认证信息到SecurityContext... } filterChain.doFilter(request, response); } }
- 前端简化:登录成功后无需JS处理,直接由后端重定向即可,前端登录表单提交后等待后端响应就行。
方式二:页面加载时自动为异步请求添加Token(仅适用于页面内AJAX请求)
如果一定要用localStorage存储Token,只能解决页面内异步请求的凭证问题,无法解决页面初始加载的请求头问题:
- 登录成功后存Token到localStorage:
async function handleSuccess(response) { const token = response.data.jwt; const redirect = response.data.url; localStorage.setItem('jwt', token); // 直接跳转,不用axios请求 window.location.href = redirect; }
- 全局配置axios自动加头:在所有页面的公共JS里添加全局axios拦截器,自动从localStorage取Token加到请求头:
// 全局axios配置,放在所有页面的公共JS里 axios.interceptors.request.use(config => { const token = localStorage.getItem('jwt'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; });
注意:这种方式下,页面初始加载的请求(比如/dashboard的GET请求)还是没有Authorization头,所以后端需要允许未登录用户访问页面框架,页面内的异步接口再用Token验证。
方式三:URL传递Token(不推荐,安全性低)
可以把Token作为URL参数传递,比如window.location.href = ${redirect}?token=${token}``,然后后端从请求参数中提取Token验证。但这种方式安全性极差,Token会出现在URL日志、浏览器历史记录中,容易泄露,仅用于测试场景,禁止生产环境使用。
原代码问题说明
你之前的代码中,axios.get(redirect, { headers: headers })只是发起了一个AJAX请求,拿到响应后并没有使用,紧接着执行window.location.href发起了新的跳转请求,两个请求完全独立,所以跳转后的请求不会携带之前设置的头,这部分代码完全是多余的。
内容的提问来源于stack exchange,提问作者Domenico

