前言
在使用ABP 7.0.2的MVC 作为UI项目时,在部署后的正式环境中所有的POST,DELETE的 AJAX请求都失败了.错误显示
[WRN] The required antiforgery header value "RequestVerificationToken" is not present.
查找问题
问题排查的过程很难受,对于开发环境中是没有一点问题的,只有部署之后会出现问题.
项目是使用Docker部署的,使用Nginx作为反向代理,并且https也是部署在Nginx里面.
一开始怀疑是部署的问题,但是经过测试.不使用Docker 和Nginx问题依旧存在.那么问题就出现在代码里面了.
根据警告首先去查了文档里关于Antiforgery的设置,一切都是正常的,之后又去ABP的Github里找Antiforgery的相关问题,一一排除也并非ABP的 Antiforgery问题.
在对比了开发环境的和正式环境的日志之后找到了问题方向
在开发环境中多了
Skipping the execution of current filter as its not the most effective filter implementing the policy Microsoft.AspNetCore.Mvc.ViewFeatures.IAntiforgeryPolicy
跳过antiforgery检查的日志.
到这里就去翻ABP的源代码了在AbpValidateAntiforgeryTokenAuthorizationFilter
以及AbpAutoValidateAntiforgeryTokenAuthorizationFilter
中找到了是否验证的代码
protected virtual bool ShouldValidate(AuthorizationFilterContext context)
{
var authCookieName = _antiForgeryCookieNameProvider.GetAuthCookieNameOrNull();
//Always perform antiforgery validation when request contains authentication cookie
if (authCookieName != null &&
context.HttpContext.Request.Cookies.ContainsKey(authCookieName))
{
return true;
}
var antiForgeryCookieName = _antiForgeryCookieNameProvider.GetAntiForgeryCookieNameOrNull();
//No need to validate if antiforgery cookie is not sent.
//That means the request is sent from a non-browser client.
//See https://github.com/aspnet/Antiforgery/issues/115
if (antiForgeryCookieName != null &&
!context.HttpContext.Request.Cookies.ContainsKey(antiForgeryCookieName))
{
return false;
}
// Anything else requires a token.
return true;
}
protected override bool ShouldValidate(AuthorizationFilterContext context)
{
if (!_options.AutoValidate)
{
return false;
}
if (context.ActionDescriptor.IsControllerAction())
{
var controllerType = context.ActionDescriptor
.AsControllerActionDescriptor()
.ControllerTypeInfo
.AsType();
if (!_options.AutoValidateFilter(controllerType))
{
return false;
}
}
if (IsIgnoredHttpMethod(context))
{
return false;
}
return base.ShouldValidate(context);
}
这里写了不验证AntiForgery的情况.
在代码里的配置也是没有问题.推断出问题应该在Cookie传参的问题了,打开浏览器发现没有XSRF-TOKEN这个Cookie.
但是在abp的文档里.XSEF-TOKEN是自动设置的.abp文档
Github也没有相关issue.无奈只能去看源码
在源码里找设置Cookie的地方
在AspNetCoreAbpAntiForgeryManager
里写了方法
public virtual void SetCookie()
{
HttpContext.Response.Cookies.Append(
Options.TokenCookie.Name,
GenerateToken(),
Options.TokenCookie.Build(HttpContext)
);
}
再找到引用该方法的地方.发现有四个,随便打开一个
AbpApplicationConfigurationController
[HttpGet]
public virtual async Task<ApplicationConfigurationDto> GetAsync(
ApplicationConfigurationRequestOptions options)
{
_antiForgeryManager.SetCookie();
return await _applicationConfigurationAppService.GetAsync(options);
}
这个方法是所有页面都会使用到的,也就是自动设置了Cookie.
在日志的开头往前的部分看到Cookie设置错误的警告
[WRN] The cookie 'XSRF-TOKEN' has set 'SameSite=None' and must also set 'Secure'.
两个警告差的相当远,现在看应该是Secure的问题了
开测试项目验证一下
验证结果
设置了SameSite=None
但是没有设置 Secure=true
在我使用的版本.NET 7
中会阻止生成Cookie
代码验证
HttpContext.Response.Cookies.Append("test", "123", new CookieOptions
{
SameSite = SameSiteMode.None,
Secure = false
});
在ASP.NET 中的警告内容
如果设置了secure=true就正常设置了
解决方案
改代码加上Secure=ture
但是这一段代码是ABP中的源码.不好轻易改,
就有了下面的解决办法
添加CookiePolicy
app.UseCookiePolicy(new CookiePolicyOptions
{
Secure = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always
}) ;
这样所有的Cookie都会被设置成Secure=true