.Net Core中AuthorizationHandlerContext如何获取当前请求的相关信息

Posted 娃都会打酱油了

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.Net Core中AuthorizationHandlerContext如何获取当前请求的相关信息相关的知识,希望对你有一定的参考价值。

.Net Core中要自定义用户身份认证,需要实现IAuthorizationHandler,实现的代码也比较简单,一般我们只要实现本地认证AuthorizationHandler<T>.HandleRequirementAsync即可,认证时一般需要用到一些用于判断是否允许访问的认证信息,比如当前的用户信息,比如当前请求的资源信息,这些信息呢,我们都可以通过AuthorizationHandlerContext来获取。

AuthorizationHandlerContext.Resource对应当前请求的资源信息,其返回值为object,所以我们也不知道这个值究竟是什么东西,但没关系,我们可以通过调试阶段的快速监视来查看实际Resource究竟是什么。

可以看到其类型为Microsoft.AspNetCore.Routing.RouteEndpointEndpoint.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")));
                        });
                });

然后在要限制的controlleraction上添加[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如何获取当前请求的相关信息的主要内容,如果未能解决你的问题,请参考以下文章

Asp.net core 配置文件

.net core系列之《.net core中使用MySql以及Dapper》

如果 .Net Core 可以在 Windows 上运行,为啥不能在 .Net Framework 中引用 .Net Core DLL?

redis(在.net core中)

.net core app.UseHsts()

Net core学习系列——Net Core配置