造轮子之自定义授权策略

轮子,自定义,授权,策略 · 浏览次数 : 33

小编点评

**授权策略** * 使用 `PermissionAuthorizationHandler` 处理授权请求。 * `PermissionAuthorizationHandler` 检查 `ControllerName` 和 `ActionName`,并使用 `IPermissionChecker` 进行授权检查。 * `PermissionAuthorizationPolicyProvider` 用于在匹配到自定义 policy 时使用 `PermissionAuthorizationHandler` 进行授权校验。 **IPermissionChecker 接口** * 检查用户角色是否在指定控制器和动作上具有授权。 * 使用分布式缓存存储用户角色权限。 * 根据角色权限判断授权结果。 **PermissionAuthorizationHandler 接口** * 重写 `HandleRequirementAsync` 方法,进行授权检查。 * 如果通过,将 `AuthorizationPolicy` 设置为 `Permit`。 * 如果不通过,将 `AuthorizationPolicy` 设置为 `Forbidden`,并使用 `context.Fail` 方法退出处理。 **PermissionAuthorizationPolicyProvider 接口** * 在匹配到自定义 policy 时,使用 `PermissionAuthorizationHandler` 进行授权检查。 * 如果匹配到自定义 policy,设置 `AuthorizationPolicy`。 * 否则,设置 `AuthorizationPolicy` 为 `Default`,表示不进行授权。 **WheelControllerBase 抽象控制器** * 添加 `[Authorize(\"Permission\")]` 特性头部。 * 提供 `HandleAuthorize` 方法,用于实际授权检查。 **测试示例** ```csharp [Authorize("Permission")] public class MyController : WheelControllerBase { // ... } ``` 在 `MyController` 中,使用 `[Authorize]` 标记所有方法。 在 `HandleAuthorize` 方法中,使用 `IPermissionChecker` 检查用户角色。

正文

前面我们已经弄好了用户角色这块内容,接下来就是我们的授权策略。在asp.net core中提供了自定义的授权策略方案,我们可以按照需求自定义我们的权限过滤。
这里我的想法是,不需要在每个Controller或者Action打上AuthorizeAttribute,自动根据ControllerName和ActionName匹配授权。只需要在Controller基类打上一个AuthorizeAttribute,其他Controller除了需要匿名访问的,使用统一的ControllerName和ActionName匹配授权方案。
话不多说,开整。

IPermissionChecker

首先我们需要一个PermissionChecker来作为检查当前操作是否有权限。很简单,只需要传入ControllerName和ActionName。至于实现,后续再写。

namespace Wheel.Authorization
{
    public interface IPermissionChecker
    {
        Task<bool> Check(string controller, string action);
    }
}

PermissionAuthorizationHandler

接下来我们则需要实现一个PermissionAuthorizationHandler和PermissionAuthorizationRequirement,继承AuthorizationHandler抽象泛型类。

using Microsoft.AspNetCore.Authorization;

namespace Wheel.Authorization
{
    public class PermissionAuthorizationRequirement : IAuthorizationRequirement
    {
        public PermissionAuthorizationRequirement()
        {
        }

    }
}
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers;
using Wheel.DependencyInjection;

namespace Wheel.Authorization
{
    public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionAuthorizationRequirement>, ITransientDependency
    {
        private readonly IPermissionChecker _permissionChecker;

        public PermissionAuthorizationHandler(IPermissionChecker permissionChecker)
        {
            _permissionChecker = permissionChecker;
        }

        protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement)
        {
            if (context.Resource is HttpContext httpContext)
            {
                var actionDescriptor = httpContext.GetEndpoint()?.Metadata.GetMetadata<ControllerActionDescriptor>();
                var controllerName = actionDescriptor?.ControllerName;
                var actionName = actionDescriptor?.ActionName;
                if (await _permissionChecker.Check(controllerName, actionName))
                {
                    context.Succeed(requirement);
                }
            }
        }
    }
}

在PermissionAuthorizationHandler中注入IPermissionChecker。
然后通过重写HandleRequirementAsync进行授权策略的校验。
这里使用HttpContext获取请求的ControllerName和ActionName,再使用IPermissionChecker进行检查,如果通过则放行,不通过则自动走AspNetCore的其他AuthorizationHandler流程,不需要调用context.Fail方法。

PermissionAuthorizationPolicyProvider

这里除了AuthorizationHandler,还需要实现一个PermissionAuthorizationPolicyProvider,用于在匹配到我们自定义Permission的时候,就使用PermissionAuthorizationHandler做授权校验,否则不会生效。

using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Wheel.DependencyInjection;

namespace Wheel.Authorization
{
    public class PermissionAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider, ITransientDependency
    {
        public PermissionAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options) : base(options)
        {
        }
        public override async Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
        {
            var policy = await base.GetPolicyAsync(policyName);
            if (policy != null)
            {
                return policy;
            }
            if (policyName == "Permission")
            {
                var policyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
                policyBuilder.AddRequirements(new PermissionAuthorizationRequirement());
                return policyBuilder.Build();
            }
            return null;
        }
    }
}

很简单,只需要匹配到policyName == "Permission"时,添加一个PermissionAuthorizationRequirement即可。

PermissionChecker

接下来我们来实现IPermissionChecker的接口。

namespace Wheel.Permission
{
    public class PermissionChecker : IPermissionChecker, ITransientDependency
    {
        private readonly ICurrentUser _currentUser;
        private readonly IDistributedCache _distributedCache;

        public PermissionChecker(ICurrentUser currentUser, IDistributedCache distributedCache)
        {
            _currentUser = currentUser;
            _distributedCache = distributedCache;
        }

        public async Task<bool> Check(string controller, string action)
        {
            if (_currentUser.IsInRoles("admin"))
                return true;
            foreach (var role in _currentUser.Roles)
            {
                var permissions = await _distributedCache.GetAsync<List<string>>($"Permission:R:{role}");
                if (permissions is null)
                    continue;
                if (permissions.Any(a => a == $"{controller}:{action}"))
                    return true;
            }
            return false;
        }
    }
}

通过当前请求用户ICurrentUser以及分布式缓存IDistributedCache做权限判断,避免频繁查询数据库。
这里ICurrentUser如何实现后续文章再写。
很简单,先判断用户角色是否是admin,如果是admin角色则默认所有权限放行。否则根据缓存中的角色权限进行判断。如果通过则放行,否则拒绝访问。

创建抽象Controller基类

创建WheelControllerBase抽象基类,添加[Authorize("Permission")]的特性头部,约定其余所有Controller都继承这个控制器。

    [Authorize("Permission")]
    public abstract class WheelControllerBase : ControllerBase
    {
        
    }

接下来我们测试一个需要权限的API。
image.png
image.png
image.png
image.png
通过DEBUG可以看到我们正常走了校验并响应401。

就这样我们完成了我们自定义的授权策略配置。

轮子仓库地址https://github.com/Wheel-Framework/Wheel
欢迎进群催更。

image.png

与造轮子之自定义授权策略相似的内容:

造轮子之自定义授权策略

前面我们已经弄好了用户角色这块内容,接下来就是我们的授权策略。在asp.net core中提供了自定义的授权策略方案,我们可以按照需求自定义我们的权限过滤。这里我的想法是,不需要在每个Controller或者Action打上AuthorizeAttribute,自动根据ControllerName和

造轮子之权限管理

上文已经完成了自定义授权策略,那么接下来就得完善我们的权限管理了。不然没有数据,如何鉴权~ 表设计 创建我们的表实体类: namespace Wheel.Domain.Permissions { public class PermissionGrant : Entity { public

造轮子之菜单管理

前面完成了基础管理的相关API,接下来就得做一个菜单管理了,用于对接管理后台前端界面。 设计菜单结构 菜单是一个多级结构,所以我们得设计一个树形的。包含自己上级和下级的属性。同时预留Permission用于做可选的权限限制。 namespace Wheel.Domain.Menus { ///

动手造轮子自己实现人工智能神经网络(ANN),解决鸢尾花分类问题Golang1.18实现

人工智能神经网络( Artificial Neural Network,又称为ANN)是一种由人工神经元组成的网络结构,神经网络结构是所有机器学习的基本结构,换句话说,无论是深度学习还是强化学习都是基于神经网络结构进行构建。关于人工神经元,请参见:人工智能机器学习底层原理剖析,人造神经元,您一定能看

我的OpenAI库发布了!!!

chatGPT正式发布已经有段时间了,这段时间我也深度体验了chatGPT的魅力。 OpenAI除了提供网页版的chatGPT,还通过api的形式提供了很多其它服务,包括文字纠错、图片生成、音频转换等等。 作为程序员,即使有现成的openai库,但还是免不了想自己造轮子,所以就有这个openai库。

前端文件上传的几种交互造轮子

前端文件上传本来是一个常规交互操作,没什么特殊性可言,但是最近在做文件上传,需要实现截图粘贴上传,去找了下有没有什么好用的组件,网上提供的方法有,但是没找完整的组件来支持cv上传,经过了解发现可以用剪贴板功能让自己的cv实现文件上传,于是自己就整合了目前几种文件上传的交互方式,码了一个支持cv的vue3文件上传组件(造个轮子)。

Java:既然有了synchronized,为什么还要提供Lock?

摘要:在Java中提供了synchronized关键字来保证只有一个线程能够访问同步代码块。既然已经提供了synchronized关键字,那为何在Java的SDK包中,还会提供Lock接口呢?这是不是重复造轮子,多此一举呢? 本文分享自华为云社区《【高并发】Java中提供了synchronized,

造个Python轮子,实现根据Excel生成Model和数据导入脚本

前言 最近遇到一个需求,有几十个Excel,每个的字段都不一样,然后都差不多是第一行是表头,后面几千上万的数据,需要把这些Excel中的数据全都加入某个已经上线的Django项目 这就需要每个Excel建个表,然后一个个导入了 这样的效率太低,不能忍 所以我造了个自动生成 Model 和导入脚本的轮

我为什么还要造一个前端轮子?

一、原因 现在市面上有很多为前端开发的框架、模板。为什么我们还要再做一个呢,究其原因,因为这些框架和模板更多的基于技术层面提供了快捷方便的实现方法;但却缺少具体业务层面的实现。因此,结合自身需求,将各种系统常用的功能和页面进行归纳总结,实现了一套更贴近实际业务的前端框架,框架中提供了一系列实际的业务

造轮子之消息实时推送

前面我们的EventBus已经弄好了,那么接下来通过EventBus来实现我们的消息推送就是自然而然的事情了。说到消息推送,很多人肯定会想到Websocket,既然我们使用Asp.net core,那么SignalR肯定是我们的首选。接下来就用SignalR来实现我们的消息实时推送。 Notific