部署至EC2后Spring Boot的XSRF-TOKEN Cookie未在浏览器保存
我在AWS EC2上部署了采用会话认证授权的Spring Boot API应用,通过Nginx反向代理路由至Docker中运行的应用,已启用CSRF保护以防护POST路由(含登录路由,尽管其在安全过滤器链中设为允许所有)。另有独立EC2服务器托管Angular UI,当前实现为登录页面加载时,通过CSRF控制器向Spring应用请求获取CSRF Token。本地开发时流程正常,但部署至EC2实例后,登录请求返回401未授权,经查X-XSRF-TOKEN未保存至浏览器。禁用CSRF保护验证后,用户登录会话Cookie可正常保存。请问为何部署后X-XSRF-TOKEN无法保存?注:两个EC2实例均配置SSL证书。
相关配置
Angular拦截器
@Injectable() export class CsrfInterceptor implements HttpInterceptor { tokenExtractor: HttpXsrfTokenExtractor = inject(HttpXsrfTokenExtractor); intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const headerName = 'X-XSRF-TOKEN'; const token = this.tokenExtractor.getToken() as string; if (token !== null && !req.headers.has(headerName)) { req = req.clone({ headers: req.headers.set(headerName, token) }); } return next.handle(req); } }
Spring Boot跨域配置
CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(allowOrigins); configuration.setAllowedMethods(List.of("GET", "PUT", "POST", "DELETE", "OPTIONS")); configuration.setAllowedHeaders(List.of(CONTENT_TYPE, ACCEPT, "X-XSRF-TOKEN")); configuration.setExposedHeaders(List.of(LOCATION, "X-XSRF-TOKEN")); configuration.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration);
Cookie过滤器
@Slf4j private static final class CsrfCookieFilter extends OncePerRequestFilter { @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain ) throws ServletException, IOException { CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); response.setHeader(csrfToken.getHeaderName(), csrfToken.getToken()); filterChain.doFilter(request, response); } }
CSRF控制器
@GetMapping(path = "/csrf") public CsrfToken csrf(CsrfToken csrfToken) { return csrfToken; }
安全过滤器链中的CSRF配置
.csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()) )
Spring应用所在EC2实例的Nginx配置
server { listen 80 default_server; listen [::]:80 default_server; root /var/www/html; index index.html index.htm index.nginx-debian.html; server_name server.example.com; location / { proxy_pass http://localhost:8080; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
Cookie Bean配置
@Bean public CookieSerializer cookieSerializer() { var domain = this.environment.getProperty("${server.servlet.session.cookie.domain}"); DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer(); cookieSerializer.setCookieName("JSESSIONID"); cookieSerializer.setUseHttpOnlyCookie(true); cookieSerializer.setUseSecureCookie(true); cookieSerializer.setCookiePath("/"); cookieSerializer.setSameSite("lax"); cookieSerializer.setCookieMaxAge(3600); cookieSerializer.setDomainName(domain); var property = Optional.ofNullable(this.environment.getProperty("spring.profiles.active")); if (property.isPresent() && (property.get().equals("dev") || property.get().equals("test"))) { cookieSerializer.setUseHttpOnlyCookie(false); cookieSerializer.setUseSecureCookie(false); cookieSerializer.setCookieMaxAge(1800); } return cookieSerializer; }
本地开发时X-XSRF-TOKEN截图

结合配置和部署环境,X-XSRF-TOKEN无法保存的核心原因集中在跨域Cookie传递规则、Nginx反向代理配置缺失、CSRF Cookie属性配置错误这几个方面,逐一分析如下:
1. Nginx配置缺失关键Cookie传递头
当前Nginx配置未处理Cookie相关反向代理头,导致Spring设置的CSRF Cookie无法正确透传给前端。需补充以下配置:
location / { proxy_pass http://localhost:8080; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # 新增:传递Cookie相关头 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 允许传递Cookie proxy_cookie_path / "/; secure; SameSite=Lax"; }
X-Forwarded-Proto:告知Spring应用当前请求为HTTPS协议,确保Secure属性的Cookie能被正确设置proxy_cookie_path:修正Cookie的路径和属性,适配HTTPS环境下的跨域要求
2. CSRF Cookie的Domain配置错误
Cookie Bean中domain取值的占位符写法错误,应直接读取配置项:
// 原错误代码: var domain = this.environment.getProperty("${server.servlet.session.cookie.domain}"); // 修正为: var domain = this.environment.getProperty("server.servlet.session.cookie.domain");
若部署时未正确配置server.servlet.session.cookie.domain,CSRF Cookie的Domain会与前端域名不匹配,导致浏览器拒绝保存。需确保该配置值为Spring应用的域名(如server.example.com),且与Angular UI的域名处于同一主域。
3. CSRF Cookie的SameSite属性配置
当前Cookie Bean设置SameSite="lax",跨域请求下Lax可能不足以允许Cookie传递。可临时改为SameSite="None"(必须配合Secure属性,你的环境已满足SSL配置):
cookieSerializer.setSameSite("None");
注意:SameSite=None需要浏览器支持,且必须搭配Secure属性,否则浏览器会拒绝保存Cookie。
4. 跨域配置的AllowedOrigins需精确匹配
确保allowOrigins配置的是Angular UI的完整域名(如https://ui.example.com),不能使用通配符*——当AllowCredentials=true时,通配符会被浏览器视为无效。
5. CSRF Token的传递方式问题
Angular拦截器依赖从Cookie中提取XSRF-TOKEN(Angular默认自动读取名为XSRF-TOKEN的Cookie),而你的Cookie过滤器仅将Token放在响应头中,可能导致Angular无法自动捕获。可调整Spring的CSRF配置,确保Cookie正确生成:
.csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()) )
同时,CsrfCookieFilter可保留,但无需额外设置响应头——CookieCsrfTokenRepository已自动设置XSRF-TOKEN Cookie。
内容的提问来源于stack exchange,提问作者iTchTheRightSpot

