IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> .NET 云原生架构师训练营(权限系统 代码实现 ActionAccess)--学习笔记 -> 正文阅读

[JavaScript知识库].NET 云原生架构师训练营(权限系统 代码实现 ActionAccess)--学习笔记

目录

  • 开发任务
  • 代码实现

开发任务

  • DotNetNB.Security.Core:定义 core,models,Istore;实现 default memory store
  • DotNetNB.Security.ActionAccess:扫描 action;添加 action authorize filter;添加集成方式

代码实现

对于一个 web 项目,Filter 是在构建构建 builder 的时候添加的

builder.Services.AddControllers(options =>
{
    options.Filters.Add<>()
})

这里不可能让用户手动添加,所以需要有一个扩展方法给用户调用

using Microsoft.AspNetCore.Mvc;

namespace DotNetNB.Security.ActionAccess
{
    public static class MvcOptionsExtensions
    {
        public static MvcOptions AddActionAccessControl(this MvcOptions options)
        {
            options.Filters.Add<DynamicAuthorizeFilter>();
            return options;
        }
    }
}

创建 Resource,包含 Key 和 Data 两个属性

namespace DotNetNB.Security.Core.Models
{
    public class Resource
    {
        public string Key { get; set; }

        public object Data { get; set; }
    }
}

创建 ActionResource,继承 Resource,包含 ControllerName,ActionName,RouteTemplate 和 HttpVerb 几个属性

using DotNetNB.Security.Core.Models;

namespace DotNetNB.Security.ActionAccess
{
    public class ActionResource : Resource
    {

    }

    public class ActionResourceData
    {
        public string? ControllerName { get; set; }

        public string? ActionName { get; set; }
        
        public  string DisplayName { get; set; }

        public string? RouteTemplate { get; set; }

        public string? HttpVerb { get; set; }
    }
}

定义一个 IResourceManager 接口,提供创建资源的方法

using DotNetNB.Security.Core.Models;

namespace DotNetNB.Security.Core
{
    public interface IResourceManager
    {
        public Task CreateAsync(Resource resource);

        public Task CreateAsync(IEnumerable<Resource> resources);
    }
}

参考 ASP .NET Core 源码中的 ActionEndpointDataSourceBase:

https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/Routing/ActionEndpointDataSourceBase.cs

创建 endpoint 的时候有一个 action 的列表 ActionDescriptors

var endpoints = CreateEndpoints(_actions.ActionDescriptors.Items, Conventions);

它的类型是一个 IActionDescriptorCollectionProvider,专门用于扫描获取所有的 action

private readonly IActionDescriptorCollectionProvider _actions;

在 ActionAccess 模块的 ActionResourceProvider 中把它加进来

using Microsoft.AspNetCore.Mvc.Infrastructure;

namespace DotNetNB.Security.ActionAccess
{
    public class ActionResourceProvider
    {
        private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;

        public ActionResourceProvider(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider)
        {
            _actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
        }
    }
}

在 host 启动的时候扫描获取所有的 action 信息,定义一个 IResourceProvider 接口

namespace DotNetNB.Security.Core
{
    public interface IResourceProvider
    {
        public Task<IEnumerable<Resource>> ExecuteAsync();
    }
}

ActionResourceProvider 实现这个接口,从 ActionDescriptors 获取 action 信息,构建 ActionResourceData

public async Task<IEnumerable<Resource>> ExecuteAsync()
{
    var actions = _actionDescriptorCollectionProvider.ActionDescriptors.Items;
    var actionResources = new List<ActionResource>();

    foreach (var action in actions)
    {
        if (action is ControllerActionDescriptor)
        {
            var actionDescriptor = action as ControllerActionDescriptor;
            var httpMethod = action.EndpointMetadata.Where(m => m is HttpMethodMetadata).FirstOrDefault() as HttpMethodMetadata;

            var routeAttribute =
                actionDescriptor?.EndpointMetadata.FirstOrDefault(m => m is RouteAttribute) as RouteAttribute;

            var resourceData = new ActionResourceData();
            resourceData.HttpVerb = httpMethod?.HttpMethods.First();
            resourceData.ActionName = actionDescriptor?.ActionName;
            resourceData.ControllerName = actionDescriptor?.ControllerName;
            resourceData.RouteTemplate = routeAttribute?.Template;
            resourceData.DisplayName = action.DisplayName;

            actionResources.Add(new ActionResource()
            {
                Data = resourceData,
                Key = actionDescriptor.GetSecurityKey()
            });
        }
    }

    return await Task.FromResult(actionResources);
}

参考 ASP .NET Core 源码中的 AuthorizeFilter:

https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/Authorization/AuthorizeFilter.cs

AuthorizeFilter 中有一个 OnAuthorizationAsync 的方法,首先获取 policy 执行器,然后执行了认证,接着执行授权,根据授权结果修改 AuthorizationFilterContext,我们权限主要是 Forbidden 的结果,返回 403 即可

public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
    if (context == null)
    {
        throw new ArgumentNullException(nameof(context));
    }

    if (!context.IsEffectivePolicy(this))
    {
        return;
    }

    // IMPORTANT: Changes to authorization logic should be mirrored in security's AuthorizationMiddleware
    var effectivePolicy = await GetEffectivePolicyAsync(context);
    if (effectivePolicy == null)
    {
        return;
    }

    var policyEvaluator = context.HttpContext.RequestServices.GetRequiredService<IPolicyEvaluator>();

    var authenticateResult = await policyEvaluator.AuthenticateAsync(effectivePolicy, context.HttpContext);

    // Allow Anonymous skips all authorization
    if (HasAllowAnonymous(context))
    {
        return;
    }

    var authorizeResult = await policyEvaluator.AuthorizeAsync(effectivePolicy, authenticateResult, context.HttpContext, context);

    if (authorizeResult.Challenged)
    {
        context.Result = new ChallengeResult(effectivePolicy.AuthenticationSchemes.ToArray());
    }
    else if (authorizeResult.Forbidden)
    {
        context.Result = new ForbidResult(effectivePolicy.AuthenticationSchemes.ToArray());
    }
}

DynamicAuthorizeFilter 继承自 AuthorizeFilter,只获取 ControllerActionDescriptor 类型的 action,如果 permissions 中不包含 actionKey 则返回 403

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;

namespace DotNetNB.Security.ActionAccess
{
    public class DynamicAuthorizeFilter : AuthorizeFilter
    {
        public override async Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
            if (actionDescriptor == null)
            {
                return;
            }

            base.OnAuthorizationAsync(context);
            if (context.Result != null)
            {
                return;
            }

            var permissions = context.HttpContext.User.Claims.Where(c => c.Type == Core.ClaimsTypes.Permission);
            var actionKey = actionDescriptor.GetSecurityKey();

            var values = permissions.Select(p => p.Value);
            if (!values.Contains(actionKey))
            {
                context.Result = new ForbidResult();
            }
        }
    }
}

在 ClaimsTypes 中增加一个 Permission 类型的 ClaimsType

namespace DotNetNB.Security.Core
{
    public static class ClaimsTypes
    {
        public const string Permission = "Permission";
    }
}

为了避免重复代码,添加一个 GetSecurityKey 的扩展方法

using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Internal;

namespace DotNetNB.Security.ActionAccess;

public static string GetSecurityKey(this ControllerActionDescriptor descriptor)
{
    var httpMethod = descriptor.EndpointMetadata.FirstOrDefault(m => m is HttpMethodMetadata) as HttpMethodMetadata;

    return string.Format($"{descriptor?.ControllerName}-{descriptor?.ActionName}-{httpMethod.HttpMethods.First()}");
}

这样就完成了 DotNetNB.Security.ActionAccess 模块的 ActionResourceProvider 和 DynamicAuthorizeFilter

GitHub源码链接:

https://github.com/MingsonZheng/dotnetnb.security

?

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-02-16 12:59:54  更:2022-02-16 13:02:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:49:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码