.net core 2.0 自定义权限过滤器和忽略验证的实现方式
- 继承Microsoft.AspNetCore.Mvc.Filters.IAuthorizationFilter实现自定义的过滤器
- 在不需要过滤器验证的Action或Controller上增加特性[AllowAnonymous]
- 在过滤器的OnAuthorization方法内对该特性进行判断,包含该特性的Controller或者Action跳过验证逻辑代码。具体判断方式如下:
/// <summary>
/// 判断当前请求的context.Filters是否包含IAllowAnonymousFilter;
/// 包含代表当前Action需要忽略权限验证逻辑。
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private bool HasAllowAnonymous(AuthorizationFilterContext context)
=> context.Filters.Any(item => item is IAllowAnonymousFilter);
需要忽略登陆验证的Action写法如下:
/// <summary>
/// Action或者Controller增加特性:AllowAnonymous
/// 将会跳过过滤器的验证逻辑,HasAllowAnonymous会返回true。
/// </summary>
[HttpGet, AllowAnonymous]
public IActionResult Get()
{
var data = new
{
ts = DateTime.Now.TotalSeconds()
};
return new JsonResult(data);
}
但是以上写法,当项目升级到.net core 3.1之后,发现HasAllowAnonymous的判断失效了,也就是context.Filters中没有类型为IAllowAnonymousFilter的过滤器了,导致无法判断到指定的Controller或者Action添加了允许匿名访问的特性。
3.1失效的原因解释
本着面向百度编程的思想翻阅了众多博客,并没有得到特别完美的答复,一怒之下去github翻阅了微软的源代码(微软自己实现的一套权限验证过滤器中也用此类方式实现了判断允许匿名访问)让我们看看微软的验证方法的源代码吧!
private bool HasAllowAnonymous(AuthorizationFilterContext context)
{
var filters = context.Filters;
for (var i = 0; i < filters.Count; i++)
{
if (filters[i] is IAllowAnonymousFilter)
{
return true;
}
}
// When doing endpoint routing, MVC does not add AllowAnonymousFilters for AllowAnonymousAttributes that
// were discovered on controllers and actions. To maintain compat with 2.x,
// we'll check for the presence of IAllowAnonymous in endpoint metadata.
var endpoint = context.HttpContext.GetEndpoint();
if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
{
return true;
}
return false;
}
以上是微软的源代码,来自.net core 源码库,具体文件链接地址:https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/Authorization/AuthorizeFilter.cs
特别注意!亮点在这段英文的注释上:When doing endpoint routing, MVC does not add AllowAnonymousFilters for AllowAnonymousAttributes。
大概含义是:如果项目开启了终结点路由(Endpoint),MVC不会为AllowAnonymous特性自动添加AllowAnonymousFilter。
看到这,问题的根本原因清晰了:在.net core 2.0时代,微软默认没有开启Endpoint,也就是当Controller或者Action设置了AllowAnonymous特性,将会为请求自动添加AllowAnonymousFilter,这就回到了上文提到的2.0项目中的验证方法的写法上了。
毫不犹豫,果断的去翻了项目的Startup,果然3.0的项目默认使用了Endpoint
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
至此,问题已经非常明了了,那么如果想在3.0项目上恢复为2.0的写法应该怎么处置呢?
- 干掉app.UseEndpoints(),增加app.UseMvc()。
- 在ConfigureServices中的AddMvcOptions里,设置options.EnableEndpointRouting = false;
- 经过测试,这一番设置之后,确实AllowAnonymousFilter又回来了。
总结
解决该问题,两个办法。要么采用微软的判断方式(判断过滤器之后再从Endpoint中获取特性进行判断)、要么设置为默认关闭Endpoint。