.Net Core中AuthorizationHandlerContext如何获取当前请求的相关信息
Posted 娃都会打酱油了
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.Net Core中AuthorizationHandlerContext如何获取当前请求的相关信息相关的知识,希望对你有一定的参考价值。
在.Net Core
中要自定义用户身份认证,需要实现IAuthorizationHandler
,实现的代码也比较简单,一般我们只要实现本地认证AuthorizationHandler<T>.HandleRequirementAsync
即可,认证时一般需要用到一些用于判断是否允许访问的认证信息,比如当前的用户信息,比如当前请求的资源信息,这些信息呢,我们都可以通过AuthorizationHandlerContext
来获取。
AuthorizationHandlerContext.Resource
对应当前请求的资源信息,其返回值为object
,所以我们也不知道这个值究竟是什么东西,但没关系,我们可以通过调试阶段的快速监视来查看实际Resource
究竟是什么。
可以看到其类型为Microsoft.AspNetCore.Routing.RouteEndpoint
,Endpoint.DisplayName
为请求的action
,继续展开,可以看到Endpoint.Metadata
内包含了action
对应的Attribute
之类的信息,这些信息就是我们真正需要的内容。
PS:注意具体AuthorizationHandlerContext.Resource
具体是什么和当前应用的宿主模式有关,我们开发时用的是默认的Kestrel
模式,其它模式很可能不是Microsoft.AspNetCore.Routing.RouteEndpoint
上面说完了当前请求的资源部分,下面说当前用户信息部分,这部分就简单了,AuthorizationHandlerContext.User
直接就可以获取当前的用户信息,如果这部分信息还不够,那你也可以在AuthorizationHandler<T>
中直接注入你需要的内容,比如注入IHttpContextAccessor
来获取当前请求的上下文,比如注入IOptions<T>
来获取一些配置信息等。
下面举一个使用例子,该例子是设置接口在HttpGet
时对所有人员公开,但非HttpGet
请求时只有指定用户可以调用
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class UserLimitOperationAttribute : AuthorizeAttribute
{
/// <summary>
/// 直接指定当前的PolicyName
/// </summary>
public const string PolicyName = "UserLimitOperation";
public UserLimitOperationAttribute()
: base(PolicyName)
{
}
}
public class UserLimitOperationRequirement : IAuthorizationRequirement
{
/// <summary>
/// 如果设置了<see cref="UserLimitOperationAttribute"/>,但又没配置允许的用户,那么就认为对所有人开放(测试环境开放)
/// </summary>
public bool NoLimit
{
get
{
return this.Users.Count == 0;
}
}
public HashSet<string> Users { get; } = new HashSet<string>();
public UserLimitOperationRequirement(string limitUser)
{
if (!string.IsNullOrWhiteSpace(limitUser))
{
this.Users = limitUser.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToHashSet();
}
}
}
public class UserLimitOperationHandler : AuthorizationHandler<UserLimitOperationRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserLimitOperationRequirement requirement)
{
if (requirement.NoLimit)
{
context.Succeed(requirement);
}
else
{
var endpoint = context.Resource as RouteEndpoint;
if (endpoint != null)
{
var http = endpoint.Metadata.Where(_=>_ is HttpMethodAttribute).First() as HttpMethodAttribute;
if (http is HttpGetAttribute)
{//Get请求是公开的
context.Succeed(requirement);
return Task.CompletedTask;
}
}
if (context.User.Identity.IsAuthenticated
&& !string.IsNullOrWhiteSpace(context.User.Identity.Name) && requirement.Users.Contains(context.User.Identity.Name!))
{
//只有配置用户才允许调用
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}
Startup.ConfigureServices
注册代码如下
services.AddSingleton<IAuthorizationHandler, UserLimitOperationHandler>();
services.AddMvcCore().AddAuthorization(
options =>
{
options.AddPolicy(
UserLimitOperationAttribute.PolicyName,
policy =>
{
//这里的例子是通过IAuthorizationRequirement传递配置,也可以直接在AuthorizationHandler<T>中直接注入IOptions<T>
policy.AddRequirements(new UserLimitOperationRequirement(this.Configuration.GetValue<string>("ConfigLimitUsers")));
});
});
然后在要限制的controller
或action
上添加[UserLimitOperation]
声明即可。
当然如果你的某些配置是直接在AuthorizeAttribute
上指定的,比如指定资源标志的ResourceAuthorizeAttribute
,那在判断时,你可以通过下面的代码来读取到相应的ResourceAuthorizeAttribute
集合,然后进行逻辑判断即可。
var attributes = endpoint.Metadata.Where(_ => _ is ResourceAuthorizeAttribute).Cast<ResourceAuthorizeAttribute>().ToList();
最后再加个Log部分,个人老是忘记怎么自行指定日志的categoryName
ILoggerFactory loggerFactory;//注入
var loggerFactory.CreateLogger("limitLog");
可以扩展阅读的参考资料:
以上是关于.Net Core中AuthorizationHandlerContext如何获取当前请求的相关信息的主要内容,如果未能解决你的问题,请参考以下文章
.net core系列之《.net core中使用MySql以及Dapper》
如果 .Net Core 可以在 Windows 上运行,为啥不能在 .Net Framework 中引用 .Net Core DLL?