来源: .Net Core中AuthorizationHandlerContext如何获取当前请求的相关信息_娃都会打酱油了的博客-CSDN博客
在.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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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”)));
});
});
1
2
3
4
5
6
7
8
9
10
11
12
然后在要限制的controller或action上添加[UserLimitOperation]声明即可。
当然如果你的某些配置是直接在AuthorizeAttribute上指定的,比如指定资源标志的ResourceAuthorizeAttribute,那在判断时,你可以通过下面的代码来读取到相应的ResourceAuthorizeAttribute集合,然后进行逻辑判断即可。
var attributes = endpoint.Metadata.Where(_ => _ is ResourceAuthorizeAttribute).Cast<ResourceAuthorizeAttribute>().ToList();
1
最后再加个Log部分,个人老是忘记怎么自行指定日志的categoryName
ILoggerFactory loggerFactory;//注入
var loggerFactory.CreateLogger(“limitLog”);
1
2
可以扩展阅读的参考资料:
ASP.NET Core 认证与授权
浅析 .NET 中 AsyncLocal 的实现原理
————————————————
版权声明:本文为CSDN博主「娃都会打酱油了」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/starfd/article/details/119187464