Angular中xsrfTokenExtractor.getToken()返回null的.NET7 WebAPI CSRF问题
问题背景
API已返回XSRF-TOKEN Cookie,但Angular的XSRF拦截器中xsrfTokenExtractor.getToken()始终返回null,导致请求未携带X-XSRF-TOKEN头,被带[ValidateAntiForgeryToken]特性的接口返回400 Bad Request错误。API部署在https://localhost:7129,Angular运行在http://127.0.0.1:4210,跨域场景下出现该问题。
核心原因分析
1. 跨域同源策略限制
localhost和127.0.0.1被浏览器视为不同源,加上响应头中Set-Cookie包含SameSite=Strict属性,浏览器会阻止跨域请求携带该Cookie,导致Angular无法读取到XSRF-TOKEN。同时存在两个Set-Cookie条目,其中一个带HttpOnly属性,前端完全无法读取,进一步混淆了Cookie的有效性。
2. CORS配置错误
当前CORS策略使用AllowAnyOrigin(),但当请求需要携带凭证(如Cookie)时,浏览器不允许Access-Control-Allow-Origin设为*,会直接阻止凭证发送,导致Cookie无法被Angular读取。
3. Angular拦截器配置缺陷
拦截器仅在拿到token时才设置withCredentials: true,但实际上如果没有先携带凭证发送请求,浏览器根本不会返回Cookie,导致getToken()永远拿不到值,形成恶性循环。
4. 后端Cookie重复设置
代码中手动调用context.Response.Cookies.Append("XSRF-TOKEN", ...),同时Antiforgery中间件自动生成了另一个带HttpOnly的Cookie,造成响应头中Cookie重复,无效Cookie干扰了有效Cookie的读取。
解决方案
后端(.NET 7)调整
修改CORS策略
指定具体前端源并允许携带凭证:
builder.Services.AddCors(options => { options.AddPolicy(name: "MyCors", builder => { builder.WithOrigins("http://127.0.0.1:4210", "http://localhost:4210") .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); }); });
修正Antiforgery配置
统一Cookie规则,避免重复设置:
builder.Services.AddAntiforgery(options => { options.HeaderName = "X-XSRF-TOKEN"; options.Cookie.Name = "XSRF-TOKEN"; options.Cookie.SameSite = SameSiteMode.Lax; // 跨域场景可设为SameSiteMode.None(需配合HTTPS) options.Cookie.HttpOnly = false; });
移除手动设置Cookie的代码
删除app.Use(...)中手动Append Cookie的逻辑,Antiforgery中间件会自动处理Token的生成与Cookie设置。
前端(Angular)调整
修改XSRF拦截器
确保所有请求都携带凭证:
@Injectable() export class XsrfInterceptor implements HttpInterceptor { constructor(private xsrfTokenExtractor: HttpXsrfTokenExtractor) { } intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { // 先克隆请求并开启凭证携带 const baseRequest = req.clone({ withCredentials: true }); const xsrfToken = this.xsrfTokenExtractor.getToken(); if (xsrfToken) { return next.handle(baseRequest.clone({ headers: baseRequest.headers.set('X-XSRF-TOKEN', xsrfToken) })); } return next.handle(baseRequest); } }
关键结论
前后端域名(localhost与127.0.0.1)不一致确实是导致问题的核心因素之一,结合CORS配置错误、Cookie属性不匹配、拦截器逻辑缺陷,最终造成XSRF Token无法被正确读取和携带。
内容的提问来源于stack exchange,提问作者jonantony

