「源码」基于.NET6的跨平台的低代码分布式RBAC Web 后台管理系统_科技通讯_闲暇巴

mikel阅读(463)

来源: 「源码」基于.NET6的跨平台的低代码分布式RBAC Web 后台管理系统_科技通讯_闲暇巴

今天给大家分享一款前后端分离的、代码能够自动生成的、基于.NET6的、可以跨平台的、低代码、分布式RBAC Web 后台管理系统,支持系统集成与统一认证。前端采用Vue Element Admin,后端采用服务与仓储模式——她就是RuYiAdmin。

软件架构

「源码」基于.NET6的跨平台的低代码分布式RBAC Web 后台管理系统

技术栈

「源码」基于.NET6的跨平台的低代码分布式RBAC Web 后台管理系统

项目优势

前端
框架	
	使用Vue2
	UI采用Element UI
	框架使用Vue Element Admin
	对于通用API做了统一性的封装
	对于BasePage界面与通用方法做了较为全面的封装
	支持水印
	支持锁屏
后端
框架	
	采用.Net6平台,支持跨平台
	采用 Core Web API,既可用于构建独立服务,也可以用于支撑前端业务
	支持Linux Docker
	使用Swagger作为Web API的管理工具	
	为接口与Model做了精细的注释
	使用JwtSecurity验证,防止Web API滥调
	使用AutoFac,实现依赖自动注入
	支持log4net 
	支持控制台日志输出
	支持Quartz作业调度
	支持API路由白名单
	支持Request Headers验证自由配置
	封装了统一的基类模型
	支持个性化导出Excel
	支持Excel导入常规校验
	支持相对路径、绝对路径或NAS存储
	支持AutoMapper,实现POCO与DTO自动转化
	使用地表最强ORM——SqlSugar,采用单例模式,支持多种数据库、包括国产数据库
	封装、实现了灵活高效的SQL访问底层
	支持实时消息
	封装了统一的数据返回格式
	封装了强大的查询条件,支持前端的自定义查询
	支持禁用用户实时踢出系统
	封装了前端UI、后端控制层、服务层与仓储层通用逻辑,前、后端业务代码量少,
开启极简代码时代。
	支持按钮、视图的可见性控制
	支持按钮与视图级别的颗粒授权
	支持权限下放
	支持用户行为的AOP自动监控
	支持用户行为的AOP自动鉴权
	支持业务数据与不同机构分离
	支持菜单多语
	支持配置信息热加载、热更新
	支持SQL热加载、热更新, 支持SQL与代码分离
	支持一主两从的读写分离
	支持毫秒时间戳
	支持乐观锁并发控制
	支持大数据写入、更新
	支持海量数据写入、更新
	支持数据的逻辑与物理删除
	支持线程池、多任务
	支持任务配置与调度
	支持服务熔断与降级
	支持熔断邮件告警
	支持Consul服务治理与健康检查
	支持并发限制
	是一个完整的低代码RBAC系统管理模板 
	支持自动生成代码
	支持系统集成
	支持统一认证
	业务全面使用高速缓存,系统快到飞起来
数据库	
	构建了可视化的ER关系模型
	提供一键执行的数据库初始化脚本
中间件	
	集成了Redis,支持哨兵模式集群
	集成了ActiveMQ,支持Master Slave和Broker Cluster结合的MQ集群

系统基础功能

「源码」基于.NET6的跨平台的低代码分布式RBAC Web 后台管理系统

系统设计思想

「源码」基于.NET6的跨平台的低代码分布式RBAC Web 后台管理系统

安装教程

1.安装Redis。
2.安装ActiveMQ。
3.安装Mysql数据库。作者使用的是mysql8,如果搭建基于mycat的读写分离集群,建议使用
mysql5.7。
4.安装NodeJs。
5.克隆代码。
6.初始化数据库。结构与数据位于webapi/AppData/DataModel、DataStructrue下。如果
使用低版本mysql,可能需要修改数据库初始化脚本。
7.修改配置。修改后端appsettings.json中Redis、ActiveMQ、Mysql数据库连接串。前端
mq配置位于src/constants/active-mq.js。
8.系统前端。前端管理目录位于webapi/wwwroot。推荐使用Visual Studio Code开源软件
编辑。前端的使用可以参看Vue Element Admin官网。
9.系统后端。后端采用Visual Studio 2022开发工具,请使用最新版本。

系统截图

  1. 星空粒子登录页(默认密码:123456)
  2. 系统首页
  3. 机构管理
  4. 用户管理
  5. 菜单管理
  6. 角色管理
  7. 数据字典
  8. 审计日志
  9. 导入配置
  10. 在线用户管理
  11. 在线任务调度
  12. 系统多语管理
  13. 服务器监控
  14. swagger
  15. 系统集成及统一认证
  • 系统集成
  • 统一授权
  • 一对多授权
  • 统一认证
  • 统一访问
  1. 其他
  • 数据导入合法性校验

「源码」基于.NET6的跨平台的低代码分布式RBAC Web 后台管理系统「源码」基于.NET6的跨平台的低代码分布式RBAC Web 后台管理系统

  • 极简代码风格示例
    ///
    /// 日志业务层接口
    /// 
    public interface ILogService : IBaseService
    {
    }

部署架构

  1. 传统部署架构

「源码」基于.NET6的跨平台的低代码分布式RBAC Web 后台管理系统

  1. 云平台冗余部署架构

「源码」基于.NET6的跨平台的低代码分布式RBAC Web 后台管理系统

VsCode搭建vue通用后台管理系统vue-element-admin_Thinkingcao的博客-CSDN博客_vscode做一个管理系统

mikel阅读(486)

来源: VsCode搭建vue通用后台管理系统vue-element-admin_Thinkingcao的博客-CSDN博客_vscode做一个管理系统

vue-element-admin文档: https://panjiachen.github.io/vue-element-admin-site/zh/
在线预览: https://panjiachen.github.io/vue-element-admin/#/dashboard

Vue国内克隆:
vue-admin-template:
git clone https://gitee.com/panjiachen/vue-admin-template.git

vue-element-admin:
git clone https://gitee.com/panjiachen/vue-element-admin.git

一、vue-element-admin介绍
vue-element-admin 是一个后台前端解决方案,它基于 vue 和 element-ui实现。它使用了最新的前端技术栈,内置了i18国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。

建议

本项目的定位是后台集成方案,不太适合当基础模板来进行二次开发。因为本项目集成了很多你可能用不到的功能,会造成不少的代码冗余。如果你的项目不关注这方面的问题,也可以直接基于它进行二次开发。
集成方案: vue-element-admin
基础模板: vue-admin-template
桌面终端: electron-vue-admin
Typescript 版: vue-typescript-admin-template (鸣谢: @Armour)
Others: awesome-project

二、功能
登录 / 注销

权限验证

页面权限
指令权限
权限配置
二步登录
多环境发布

dev sit stage prod
全局功能

国际化多语言
多种动态换肤
动态侧边栏(支持多级路由嵌套)
动态面包屑
快捷导航(标签页)
Svg Sprite 图标
本地/后端 mock 数据
Screenfull全屏
自适应收缩侧边栏
编辑器

富文本
Markdown
JSON 等多格式
Excel

导出excel
导入excel
前端可视化excel
导出zip
表格

动态表格
拖拽表格
内联编辑
错误页面

401
404
組件

头像上传
返回顶部
拖拽Dialog
拖拽Select
拖拽看板
列表拖拽
SplitPane
Dropzone
Sticky
CountTo
综合实例

错误日志

Dashboard

引导页

ECharts 图表

Clipboard(剪贴复制)

Markdown2html

三、前序准备
你需要在本地安装 node和 git。本项目技术栈基于 ES2015+、vue、vuex、vue-router 、vue-cli、axios 和 element-ui,所有的请求数据都使用Mock.js进行模拟,提前了解和学习这些知识会对使用本项目有很大的帮助。

同时配套一个系列的教程文章,如何从零构建一个完整的管理后台项目,建议大家先看完这些文章再来实践本项目。

手摸手,带你用 vue 撸后台 系列一(基础篇)
手摸手,带你用 vue 撸后台 系列二(登录权限篇)
手摸手,带你用 vue 撸后台 系列三 (实战篇)
手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板)
手摸手,带你用 vue 撸后台 系列五(v4.0 新版本)
手摸手,带你封装一个 vue component
手摸手,带你优雅的使用 icon
手摸手,带你用合理的姿势使用 webpack4(上)
手摸手,带你用合理的姿势使用 webpack4(下)
四、目录结构
本项目已经为你生成了一个完整的开发框架,提供了涵盖中后台开发的各类功能和坑位,下面是整个项目的目录结构。

VSCode的个人知识管理和共享系统-JavaScript
zip

0星
超过10%的资源
21.79MB

下载
├── build # 构建相关
├── mock # 项目mock 模拟数据
├── plop-templates # 基本模板
├── public # 静态资源
│ │── favicon.ico # favicon图标
│ └── index.html # html模板
├── src # 源代码
│ ├── api # 所有请求
│ ├── assets # 主题 字体等静态资源
│ ├── components # 全局公用组件
│ ├── directive # 全局指令
│ ├── filters # 全局 filter
│ ├── icons # 项目所有 svg icons
│ ├── lang # 国际化 language
│ ├── layout # 全局 layout
│ ├── router # 路由
│ ├── store # 全局 store管理
│ ├── styles # 全局样式
│ ├── utils # 全局公用方法
│ ├── vendor # 公用vendor
│ ├── views # views 所有页面
│ ├── App.vue # 入口页面
│ ├── main.js # 入口文件 加载组件 初始化等
│ └── permission.js # 权限管理
├── tests # 测试
├── .env.xxx # 环境变量配置
├── .eslintrc.js # eslint 配置项
├── .babelrc # babel-loader 配置
├── .travis.yml # 自动化CI配置
├── vue.config.js # vue-cli 配置
├── postcss.config.js # postcss 配置
└── package.json # package.json

五、安装vue-element-admin
1. 克隆项目
git clone https://github.com/PanJiaChen/vue-element-admin.git
1
2. 进入项目目录
cd vue-element-admin
1
3.安装依赖
npm install
1
4. 设置淘宝镜像源
建议不要用 cnpm 安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题

npm install –registry=https://registry.npm.taobao.org
1
5. 本地开发 启动项目
npm run dev
1
六、访问
启动完成后会自动打开浏览器访问 http://localhost:9527, 你看到下面的页面就代表操作成功了。

————————————————
版权声明:本文为CSDN博主「Thinkingcao」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Thinkingcao/article/details/107765365

Error: Cannot find module '@vue/cli-plugin-babel'_前端段的博客-CSDN博客_@vue/cli-plugin-babel

mikel阅读(825)

来源: Error: Cannot find module ‘@vue/cli-plugin-babel’_前端段的博客-CSDN博客_@vue/cli-plugin-babel

运行npm run serve报错:
Error: Cannot find module ‘@vue/cli-plugin-babel’

解决办法:
1.安装开发依赖:
npm install babel-plugin-import -D

2.查看package.json里面的版本号,安装对应版本试试。
————————————————
版权声明:本文为CSDN博主「前端段」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hkduan/article/details/105772792

使用手机控制PPT播放实现方法(含源码)_humbinal的博客-CSDN博客

mikel阅读(909)

来源: 使用手机控制PPT播放实现方法(含源码)_humbinal的博客-CSDN博客

本文教你如何实现通过手机控制PC的幻灯片放映。

本篇文章是基于我的上一篇文章,上一篇文章中分享了这样一个小工具,这里把源码什么的分享出来。

本工具的制作使用Node.JS以及WebSocket技术,大致如下:

首先是搭建Node.JS平台,这里就不在说了,网上教程很多,我们从模块安装开始:

1.创建项目文件夹,在该文件夹下运行CMD,输入:

nmp install express

完成后继续:

npm install socket.io

以及:

npm install ejs

到这里我们需要使用的模块就装好了,我使用了EJS作为模板引擎配合express使用,socket.io用来进行WebSocket通信。

下载下面的附件一,解压后修改public文件夹中js文件夹下的script.js(第二行) 中的网址为你的网址即可。

下载附件二修改文件中src文件中remote.js(第三行)中的网址为你的网址即可。

这是启动你的服务就可以开始运行了,由于文件稍多,这里就不在一一分析具体的代码了,直接下载查看吧,有什么问题欢迎留言咨询。

这是可以手机打开sample.com;

放映PPT的电脑使用IE浏览器打开附件二中的ppt.html ;

具体使用详情见我的上一篇教程。

尽情的使用这样一款装逼神器吧!

源码下载地址:humbinal/ppt-controller (github.com)
————————————————
版权声明:本文为CSDN博主「humbinal」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012234419/article/details/45618517

ASP.NET Core MVC 授权的扩展:自定义 Authorize Attribute 和 IApplicationModelProvide - alby - 博客园

mikel阅读(676)

来源: ASP.NET Core MVC 授权的扩展:自定义 Authorize Attribute 和 IApplicationModelProvide – alby – 博客园

一、概述

ASP.NET Core MVC 提供了基于角色( Role )、声明( Chaim ) 和策略 ( Policy ) 等的授权方式。在实际应用中,可能采用部门( Department , 本文采用用户组 Group )、职位 ( 可继续沿用 Role )、权限( Permission )的方式进行授权。要达到这个目的,仅仅通过自定义 IAuthorizationPolicyProvider 是不行的。本文通过自定义 IApplicationModelProvide 进行扩展。

二、PermissionAuthorizeAttribute : IPermissionAuthorizeData

AuthorizeAttribute 类实现了 IAuthorizeData 接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace Microsoft.AspNetCore.Authorization
{
  /// <summary>
  /// Defines the set of data required to apply authorization rules to a resource.
  /// </summary>
  public interface IAuthorizeData
  {
   /// <summary>
   /// Gets or sets the policy name that determines access to the resource.
   /// </summary>
   string Policy { get; set; }
   /// <summary>
   /// Gets or sets a comma delimited list of roles that are allowed to access the resource.
   /// </summary>
   string Roles { get; set; }
   /// <summary>
   /// Gets or sets a comma delimited list of schemes from which user information is constructed.
   /// </summary>
   string AuthenticationSchemes { get; set; }
  }
}

使用 AuthorizeAttribute 不外乎如下几种形式:

1
2
3
4
[Authorize]
[Authorize("SomePolicy")]
[Authorize(Roles = "角色1,角色2")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

当然,参数还可以组合起来。另外,Roles 和 AuthenticationSchemes 的值以半角逗号分隔,是 Or 的关系;多个 Authorize 是 And 的关系;Policy 、Roles 和 AuthenticationSchemes 如果同时使用,也是 And 的关系。

如果要扩展 AuthorizeAttribute,先扩展 IAuthorizeData 增加新的属性:

1
2
3
4
5
public interface IPermissionAuthorizeData : IAuthorizeData
{
    string Groups { get; set; }
    string Permissions { get; set; }
}

然后定义 AuthorizeAttribute:

1
2
3
4
5
6
7
8
9
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class PermissionAuthorizeAttribute : Attribute, IPermissionAuthorizeData
{
    public string Policy { get; set; }
    public string Roles { get; set; }
    public string AuthenticationSchemes { get; set; }
    public string Groups { get; set; }
    public string Permissions { get; set; }
}

现在,在 Controller 或 Action 上就可以这样使用了:

1
2
3
[PermissionAuthorize(Roles = "经理,副经理")] // 经理或部门经理
[PermissionAuthorize(Groups = "研发部,生产部", Roles = "经理"] // 研发部经理或生成部经理。Groups 和 Roles 是 `And` 的关系。
[PermissionAuthorize(Groups = "研发部,生产部", Roles = "经理", Permissions = "请假审批"] // 研发部经理或生成部经理,并且有请假审批的权限。Groups 、Roles 和 Permission 是 `And` 的关系。

数据已经准备好,下一步就是怎么提取出来。通过扩展 AuthorizationApplicationModelProvider 来实现。

三、PermissionAuthorizationApplicationModelProvider : IApplicationModelProvider

AuthorizationApplicationModelProvider 类的作用是构造 AuthorizeFilter 对象放入 ControllerModel 或 ActionModel 的 Filters 属性中。具体过程是先提取 Controller 和 Action 实现了 IAuthorizeData 接口的 Attribute,如果使用的是默认的DefaultAuthorizationPolicyProvider,则会先创建一个 AuthorizationPolicy 对象作为 AuthorizeFilter 构造函数的参数。
创建 AuthorizationPolicy 对象是由 AuthorizationPolicy 的静态方法 public static async Task<AuthorizationPolicy> CombineAsync(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authorizeData) 来完成的。该静态方法会解析 IAuthorizeData 的数据,但不懂解析 IPermissionAuthorizeData

因为 AuthorizationApplicationModelProvider 类对 AuthorizationPolicy.CombineAsync 静态方法有依赖,这里不得不做一个类似的 PermissionAuthorizationApplicationModelProvider 类,在本类实现 CombineAsync 方法。暂且不论该方法放在本类是否合适的问题。

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
       public static AuthorizeFilter GetFilter(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authData)
       {
           // The default policy provider will make the same policy for given input, so make it only once.
           // This will always execute synchronously.
           if (policyProvider.GetType() == typeof(DefaultAuthorizationPolicyProvider))
           {
               var policy = CombineAsync(policyProvider, authData).GetAwaiter().GetResult();
               return new AuthorizeFilter(policy);
           }
           else
           {
               return new AuthorizeFilter(policyProvider, authData);
           }
       }
       private static async Task<AuthorizationPolicy> CombineAsync(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authorizeData)
       {
           if (policyProvider == null)
           {
               throw new ArgumentNullException(nameof(policyProvider));
           }
           if (authorizeData == null)
           {
               throw new ArgumentNullException(nameof(authorizeData));
           }
           var policyBuilder = new AuthorizationPolicyBuilder();
           var any = false;
           foreach (var authorizeDatum in authorizeData)
           {
               any = true;
               var useDefaultPolicy = true;
               if (!string.IsNullOrWhiteSpace(authorizeDatum.Policy))
               {
                   var policy = await policyProvider.GetPolicyAsync(authorizeDatum.Policy);
                   if (policy == null)
                   {
                       //throw new InvalidOperationException(Resources.FormatException_AuthorizationPolicyNotFound(authorizeDatum.Policy));
                       throw new InvalidOperationException(nameof(authorizeDatum.Policy));
                   }
policyBuilder.Combine(policy);
                   useDefaultPolicy = false;
               }
               var rolesSplit = authorizeDatum.Roles?.Split(',');
               if (rolesSplit != null && rolesSplit.Any())
               {
                   var trimmedRolesSplit = rolesSplit.Where(r => !string.IsNullOrWhiteSpace(r)).Select(r => r.Trim());
                   policyBuilder.RequireRole(trimmedRolesSplit);
                   useDefaultPolicy = false;
               }
               if(authorizeDatum is IPermissionAuthorizeData permissionAuthorizeDatum )
               {
                   var groupsSplit = permissionAuthorizeDatum.Groups?.Split(',');
                   if (groupsSplit != null && groupsSplit.Any())
                   {
                       var trimmedGroupsSplit = groupsSplit.Where(r => !string.IsNullOrWhiteSpace(r)).Select(r => r.Trim());
                       policyBuilder.RequireClaim("Group", trimmedGroupsSplit); // TODO: 注意硬编码
                       useDefaultPolicy = false;
                   }
                   var permissionsSplit = permissionAuthorizeDatum.Permissions?.Split(',');
                   if (permissionsSplit != null && permissionsSplit.Any())
                   {
                       var trimmedPermissionsSplit = permissionsSplit.Where(r => !string.IsNullOrWhiteSpace(r)).Select(r => r.Trim());
                       policyBuilder.RequireClaim("Permission", trimmedPermissionsSplit);// TODO: 注意硬编码
                       useDefaultPolicy = false;
                   }
               }
               var authTypesSplit = authorizeDatum.AuthenticationSchemes?.Split(',');
               if (authTypesSplit != null && authTypesSplit.Any())
               {
                   foreach (var authType in authTypesSplit)
                   {
                       if (!string.IsNullOrWhiteSpace(authType))
                       {
                           policyBuilder.AuthenticationSchemes.Add(authType.Trim());
                       }
                   }
               }
               if (useDefaultPolicy)
               {
policyBuilder.Combine(await policyProvider.GetDefaultPolicyAsync());
               }
           }
           return any ? policyBuilder.Build() : null;
       }

if(authorizeDatum is IPermissionAuthorizeData permissionAuthorizeDatum ) 为扩展部分。

四、Startup

注册 PermissionAuthorizationApplicationModelProvider 服务,需要在 AddMvc 之后替换掉 AuthorizationApplicationModelProvider 服务。

1
2
services.AddMvc();
services.Replac(ServiceDescriptor.Transient<IApplicationModelProvider,PermissionAuthorizationApplicationModelProvider>());

五、Jwt 示例

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
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    private readonly JwtSecurityTokenHandler _tokenHandler = new JwtSecurityTokenHandler();
    [HttpGet]
    [Route("SignIn")]
    public async Task<ActionResult<string>> SignIn()
    {
        var user = new ClaimsPrincipal(new ClaimsIdentity(new[]
        {
            // 备注:Claim Type: Group 和 Permission 这里使用的是硬编码,应该定义为类似于 ClaimTypes.Role 的常量;另外,下列模拟数据不一定合逻辑。
            new Claim(ClaimTypes.Name, "Bob"),
            new Claim(ClaimTypes.Role, "经理"),  // 注意:不能使用逗号分隔来达到多个角色的目的,下同。
            new Claim(ClaimTypes.Role, "副经理"),
            new Claim("Group", "研发部"),
            new Claim("Group", "生产部"),
            new Claim("Permission", "请假审批"),
            new Claim("Permission", "权限1"),
            new Claim("Permission", "权限2"),
        }, JwtBearerDefaults.AuthenticationScheme));
        var token = new JwtSecurityToken(
            "SignalRAuthenticationSample",
            "SignalRAuthenticationSample",
            user.Claims,
            expires: DateTime.UtcNow.AddDays(30),
            signingCredentials: SignatureHelper.GenerateSigningCredentials("1234567890123456"));
        return _tokenHandler.WriteToken(token);
    }
    [HttpGet]
    [Route("Test")]
    [PermissionAuthorize(Groups = "研发部,生产部", Roles = "经理", Permissions = "请假审批"] // 研发部经理或生成部经理,并且有请假审批的权限。Groups 、Roles 和 Permission 是 `And` 的关系。
    public async Task<ActionResult<IEnumerable<string>>> Test()
    {
        var user = HttpContext.User;
        return new string[] { "value1", "value2" };
    }
 }

六、问题

AuthorizeFilter 类显示实现了 IFilterFactory 接口的 CreateInstance 方法:

1
2
3
4
5
6
7
8
9
10
11
12
IFilterMetadata IFilterFactory.CreateInstance(IServiceProvider serviceProvider)
{
    if (Policy != null || PolicyProvider != null)
    {
        // The filter is fully constructed. Use the current instance to authorize.
        return this;
    }

    Debug.Assert(AuthorizeData != null);
    var policyProvider = serviceProvider.GetRequiredService<IAuthorizationPolicyProvider>();
    return AuthorizationApplicationModelProvider.GetFilter(policyProvider, AuthorizeData);
}

竟然对 AuthorizationApplicationModelProvider.GetFilter 静态方法产生了依赖。庆幸的是,如果通过 AuthorizeFilter(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authorizeData) 或 AuthorizeFilter(AuthorizationPolicy policy) 创建 AuthorizeFilter 对象不会产生什么不良影响。

七、下一步

[PermissionAuthorize(Groups = "研发部,生产部", Roles = "经理", Permissions = "请假审批"] 这种形式还是不够灵活,哪怕用多个 Attribute, And 和 Or 的逻辑组合不一定能满足需求。可以在 IPermissionAuthorizeData 新增一个 Rule 属性,实现类似的效果:

1
[PermissionAuthorize(Rule = "(Groups:研发部,生产部)&&(Roles:请假审批||Permissions:超级权限)"]

通过 Rule 计算复杂的授权。

八、如果通过自定义 IAuthorizationPolicyProvider 实现?

另一种方式是自定义 IAuthorizationPolicyProvider ,不过还需要自定义 AuthorizeFilter。因为当不是使用 DefaultAuthorizationPolicyProvider 而是自定义 IAuthorizationPolicyProvider 时,AuthorizationApplicationModelProvider(或前文定义的 PermissionAuthorizationApplicationModelProvider)会使用 AuthorizeFilter(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authorizeData) 创建 AuthorizeFilter 对象,而不是 AuthorizeFilter(AuthorizationPolicy policy)。这会造成 AuthorizeFilter 对象在 OnAuthorizationAsync 时会间接调用 AuthorizationPolicy.CombineAsync 静态方法。

这可以说是一个设计上的缺陷,不应该让 AuthorizationPolicy.CombineAsync 静态方法存在,哪怕提供个 IAuthorizationPolicyCombiner 也好。另外,上文提到的 AuthorizationApplicationModelProvider.GetFilter 静态方法同样不是一种好的设计。等微软想通吧。

参考资料

https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/iauthorizationpolicyprovider?view=aspnetcore-2.1

 

排版问题:http://blog.tubumu.com/2018/11/28/aspnetcore-mvc-extend-authorization/

使用nginx后net core无法获取ip问题_qq_34897745的博客-CSDN博客

mikel阅读(662)

来源: 使用nginx后net core无法获取ip问题_qq_34897745的博客-CSDN博客

使用了nginx后net core获取ip地址居然全部是本地的地址,不是外网的地址

这是因为nginx转发了一次后,我们直接使用常规获取ip地址的方式就是本地的地址了

 

瞧瞧nginx的配置,然后找获取外网ip的方法

 

 

这里我们可以看到,我们配了一个real-ip,nginx会转发给你,通过请求的header获取就行了

context.HttpContext.Request.Headers[“X-Real-IP”].FirstOrDefault();

————————————————
版权声明:本文为CSDN博主「qq_34897745」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34897745/article/details/106093714

net core中获取用户请求ip地址_qq_34897745的博客-CSDN博客_.net core 获取请求ip

mikel阅读(617)

来源: net core中获取用户请求ip地址_qq_34897745的博客-CSDN博客_.net core 获取请求ip

方法一:通过注入来获取

先添加一个依赖注入

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
在控制器里边使用构造函数注入

private readonly IHttpContextAccessor _httpContextAccessor;
public TestController( IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
获取

//获取ip地址
string ipaddress = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString();

方法二:直接获取

public void OnActionExecuting(ActionExecutingContext context)
{
//获取ip地址
string ipaddress = context.HttpContext.Connection.RemoteIpAddress.ToString();
}

但是这两种写法,使用了nginx后是无法访问的

使用了nginx后无法获取ip问题:

https://blog.csdn.net/qq_34897745/article/details/106093714
————————————————
版权声明:本文为CSDN博主「qq_34897745」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34897745/article/details/106093610

ASP.NET Core 认证与授权[6]:授权策略是怎么执行的? - 雨夜朦胧 - 博客园

mikel阅读(626)

来源: ASP.NET Core 认证与授权[6]:授权策略是怎么执行的? – 雨夜朦胧 – 博客园

在上一章中,详细介绍了 ASP.NET Core 中的授权策略,在需要授权时,只需要在对应的Controler或者Action上面打上[Authorize]特性,并指定要执行的策略名称即可,但是,授权策略是怎么执行的呢?怀着一颗好奇的心,忍不住来探索一下它的执行流程。

目录

  1. MVC中的授权
  2. IPolicyEvaluator
  3. IAuthorizationService

在《(上一章》中提到,AuthorizeAttribute只是一个简单的实现了IAuthorizeData接口的特性,并且在 ASP.NET Core 授权系统中并没有使用到它。我们知道在认证中,还有一个UseAuthentication扩展方法来激活认证系统,但是在授权中并没有类似的机制。

这是因为当我们使用[Authorize]通常是在MVC中,由MVC来负责激活授权系统。本来在这个系列的文章中,我并不想涉及到MVC的知识,但是为了能更好的理解授权系统的执行,就来简单介绍一下MVC中与授权相关的知识。

MVC中的授权

当我们使用MVC时,首先会调用MVC的AddMvc扩展方法,用来注册一些MVC相关的服务:

public static IMvcBuilder AddMvc(this IServiceCollection services)
{
    var builder = services.AddMvcCore();

    builder.AddAuthorization();

    ...
}

public static IMvcCoreBuilder AddAuthorization(this IMvcCoreBuilder builder)
{
    AddAuthorizationServices(builder.Services);
    return builder;
}

internal static void AddAuthorizationServices(IServiceCollection services)
{
    services.AddAuthenticationCore();
    services.AddAuthorization();
    services.AddAuthorizationPolicyEvaluator();

    services.TryAddEnumerable(
        ServiceDescriptor.Transient<IApplicationModelProvider, AuthorizationApplicationModelProvider>());
}

在上面AddAuthorizationServices中的前三个方法都属于 ASP.NET Core 《Security》项目中提供的扩展方法,其中前两个在前面几章已经介绍过了,对于AddAuthorizationPolicyEvaluator放到后面再来介绍,我们先来看一下MVC中的AuthorizationApplicationModelProvider

AuthorizationApplicationModelProvider

在MVC中有一个ApplicationModel的概念,它用来封装ControllerFilterApiExplorer等。对应的,在MVC中还提供了一系列的ApplicationModelProvider来初始化ApplicationModel的各个部分,而AuthorizationApplicationModelProvider就是用来初始化与授权相关的部分。

public class AuthorizationApplicationModelProvider : IApplicationModelProvider
{
    public void OnProvidersExecuting(ApplicationModelProviderContext context)
    {
        foreach (var controllerModel in context.Result.Controllers)
        {
            var controllerModelAuthData = controllerModel.Attributes.OfType<IAuthorizeData>().ToArray();
            if (controllerModelAuthData.Length > 0)
            {
                controllerModel.Filters.Add(GetFilter(_policyProvider, controllerModelAuthData));
            }
            foreach (var attribute in controllerModel.Attributes.OfType<IAllowAnonymous>())
            {
                controllerModel.Filters.Add(new AllowAnonymousFilter());
            }
            foreach (var actionModel in controllerModel.Actions)
            {
                var actionModelAuthData = actionModel.Attributes.OfType<IAuthorizeData>().ToArray();
                if (actionModelAuthData.Length > 0)
                {
                    actionModel.Filters.Add(GetFilter(_policyProvider, actionModelAuthData));
                }
                foreach (var attribute in actionModel.Attributes.OfType<IAllowAnonymous>())
                {
                    actionModel.Filters.Add(new AllowAnonymousFilter());
                }
            }
        }
    }
}

如上,首先查找每个Controller中实现了IAuthorizeData接口的特性,然后将其转化为AuthorizeFilter并添加到Controller的Filter集合中,紧接着再查找实现了IAllowAnonymous接口的特性,将其转化为AllowAnonymousFilter过滤器也添加到Filter集合中,然后以同样的逻辑查找Action上的特性并添加到Action的Filter集合中。

其中的关键点就是将IAuthorizeData(也就是通过我们熟悉的[Authorize]特性)转化为MVC中的AuthorizeFilter过滤器:

public static AuthorizeFilter GetFilter(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authData)
{
    if (policyProvider.GetType() == typeof(DefaultAuthorizationPolicyProvider))
    {
        var policy = AuthorizationPolicy.CombineAsync(policyProvider, authData).GetAwaiter().GetResult();
        return new AuthorizeFilter(policy);
    }
    else
    {
        return new AuthorizeFilter(policyProvider, authData);
    }
}

CombineAsync在上一章的《AuthorizationPolicy》中已经介绍过了,我们往下看看AuthorizeFilter的实现。

AuthorizeFilter

在MVC中有一个AuthorizeFilter过滤器,类似我们在ASP.NET 4.x中所熟悉的[Authorize],它实现了IAsyncAuthorizationFilter接口,定义如下:

public class AuthorizeFilter : IAsyncAuthorizationFilter, IFilterFactory
{
    public AuthorizeFilter(AuthorizationPolicy policy) {}
    public AuthorizeFilter(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authorizeData) : this(authorizeData) {}
    public AuthorizeFilter(IEnumerable<IAuthorizeData> authorizeData) {}

    public IEnumerable<IAuthorizeData> AuthorizeData { get; }
    public AuthorizationPolicy Policy { get; }

    public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {
        var effectivePolicy = Policy;
        if (effectivePolicy == null)
        {
            effectivePolicy = await AuthorizationPolicy.CombineAsync(PolicyProvider, AuthorizeData);
        }
        var policyEvaluator = context.HttpContext.RequestServices.GetRequiredService<IPolicyEvaluator>();
        var authenticateResult = await policyEvaluator.AuthenticateAsync(effectivePolicy, context.HttpContext);
        if (context.Filters.Any(item => item is IAllowAnonymousFilter))
        {
            return;
        }
        var authorizeResult = await policyEvaluator.AuthorizeAsync(effectivePolicy, authenticateResult, context.HttpContext, context);

        ... // 如果授权失败,返回ChallengeResult或ForbidResult
    }
}

AuthorizeFilter的OnAuthorizationAsync方法会在Action执行之前触发,其调用IPolicyEvaluator来完成授权,将执行流程切回到 ASP.NET Core 授权系统中。关于MVC中IApplicationModelProvider以及Filter的概念,在以后MVC系列的文章中再来详细介绍,下面就继续介绍 ASP.NET Core 的授权系统,也就是《Security》项目。

IPolicyEvaluator

IPolicyEvaluator是MVC调用授权系统的入口点,其定义如下:

public interface IPolicyEvaluator
{
    Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context);
    Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource);
}

在上面介绍的AddMVC中,调用了AddAuthorizationPolicyEvaluator扩展方法,它有如下定义:

public static class PolicyServiceCollectionExtensions
{
    public static IServiceCollection AddAuthorizationPolicyEvaluator(this IServiceCollection services)
    {
        services.TryAdd(ServiceDescriptor.Transient<IPolicyEvaluator, PolicyEvaluator>());
        return services;
    }
}

由此可知IPolicyEvaluator的默认实现为PolicyEvaluator,我们就从它入手,来一步一步解剖 ASP.NET Core 授权系统的执行步骤。

AuthorizeFilter中,依次调到了AuthenticateAsyncAuthorizeAsync方法,我们就一一来看。

AuthenticateAsync(AuthenticationSchemes)

为什么还有一个AuthenticateAsync方法呢,这不是在认证阶段执行的吗?我们看下它的实现:

public class PolicyEvaluator : IPolicyEvaluator
{
    public virtual async Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)
    {
        if (policy.AuthenticationSchemes != null && policy.AuthenticationSchemes.Count > 0)
        {
            ClaimsPrincipal newPrincipal = null;
            foreach (var scheme in policy.AuthenticationSchemes)
            {
                var result = await context.AuthenticateAsync(scheme);
                if (result != null && result.Succeeded)
                {
                    newPrincipal = SecurityHelper.MergeUserPrincipal(newPrincipal, result.Principal);
                }
            }

            if (newPrincipal != null)
            {
                context.User = newPrincipal;
                return AuthenticateResult.Success(new AuthenticationTicket(newPrincipal, string.Join(";", policy.AuthenticationSchemes)));
            }
            else
            {
                context.User = new ClaimsPrincipal(new ClaimsIdentity());
                return AuthenticateResult.NoResult();
            }
        }

        return (context.User?.Identity?.IsAuthenticated ?? false) 
            ? AuthenticateResult.Success(new AuthenticationTicket(context.User, "context.User"))
            : AuthenticateResult.NoResult();
    }
}

在《上一章》中,我们知道在AuthorizationPolicy中有AuthenticationSchemesIAuthorizationRequirement两个属性,并详细介绍介绍了Requirement,但是没有提到AuthenticationSchemes的调用。

那么,看到这里,也就大概明白了,它与Requirements的执行是完全独立的,并在它之前执行,用于重置Claims,那么为什么要重置呢?

在认证的章节介绍过,在认证阶段,只会执行默认的认证Scheme,context.User就是使用context.AuthenticateAsync(DefaultAuthenticateScheme)来赋值的,当我们希望使用非默认的Scheme,或者是想合并多个认证Scheme的Claims时,就需要使用基于Scheme的授权来重置Claims了。

它的实现也很简单,直接使用我们在授权策略中指定的Schemes来依次调用认证服务的AuthenticateAsync方法,并将生成的Claims合并,最后返回我们熟悉的AuthenticateResult认证结果。

AuthorizeAsync(Requirements)

接下来再看一下PolicyEvaluatorAuthorizeAsync方法:

public class PolicyEvaluator : IPolicyEvaluator
{
    private readonly IAuthorizationService _authorization;
    public PolicyEvaluator(IAuthorizationService authorization)
    {
        _authorization = authorization;
    }

    public virtual async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource)
    {
        var result = await _authorization.AuthorizeAsync(context.User, resource, policy);
        if (result.Succeeded) return PolicyAuthorizationResult.Success();
        return (authenticationResult.Succeeded) ? PolicyAuthorizationResult.Forbid() : PolicyAuthorizationResult.Challenge();
    }
}

该方法会根据Requirements来完成授权,具体的实现是通过调用IAuthorizationService来实现的。

最终返回的是一个PolicyAuthorizationResult对象,并在授权失败时,根据认证结果来返回Forbid(未授权)Challenge(未登录)

public class PolicyAuthorizationResult
{
    private PolicyAuthorizationResult() { }
    public bool Challenged { get; private set; }
    public bool Forbidden { get; private set; }
    public bool Succeeded { get; private set; }
}

IAuthorizationService

然后就到了授权的核心对象AuthorizationService,也可以称为授权的外交官,我们也可以直接在应用代码中调用该对象来实现授权,它有如下定义:

public interface IAuthorizationService
{    
    Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName);
    Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements);
}

AuthorizeAsync中还涉及到一个resource对象,用来实现面向资源的授权,放在《下一章》中再来介绍,而在本章与《前一章》的示例中,该值均为null

ASP.NET Core 中还为IAuthorizationService提供了几个扩展方法:

public static class AuthorizationServiceExtensions
{
    public static Task<AuthorizationResult> AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, string policyName) {}
    public static Task<AuthorizationResult> AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, AuthorizationPolicy policy) {}
    public static Task<AuthorizationResult> AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, object resource, IAuthorizationRequirement requirement) {}
    public static Task<AuthorizationResult> AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, object resource, AuthorizationPolicy policy) {}
}

其默认实现为DefaultAuthorizationService:

public class DefaultAuthorizationService : IAuthorizationService
{
    private readonly AuthorizationOptions _options;
    private readonly IAuthorizationHandlerContextFactory _contextFactory;
    private readonly IAuthorizationHandlerProvider _handlers;
    private readonly IAuthorizationEvaluator _evaluator;
    private readonly IAuthorizationPolicyProvider _policyProvider;

    public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName)
    {        
        var policy = await _policyProvider.GetPolicyAsync(policyName);
        return await this.AuthorizeAsync(user, resource, policy);
    }

    public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)
    {
        var authContext = _contextFactory.CreateContext(requirements, user, resource);
        var handlers = await _handlers.GetHandlersAsync(authContext);
        foreach (var handler in handlers)
        {
            await handler.HandleAsync(authContext);
            if (!_options.InvokeHandlersAfterFailure && authContext.HasFailed)
            {
                break;
            }
        }
        return _evaluator.Evaluate(authContext);
    }
}

通过上面代码可以看出,在《上一章》中介绍的授权策略,在这里获取到它的Requirements,后续便不再需要了。而在AuthorizationService中是通过调用四大核心对象来完成授权,我们一一来看。

IAuthorizationPolicyProvider

由于在[Authorize]中,我们指定的是策略的名称,因此需要使用IAuthorizationPolicyProvider来根据名称获取到策略对象,默认实现为DefaultAuthorizationPolicyProvider

public class DefaultAuthorizationPolicyProvider : IAuthorizationPolicyProvider
{
    private readonly AuthorizationOptions _options;

    public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
    {
        return Task.FromResult(_options.DefaultPolicy);
    }

    public virtual Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        return Task.FromResult(_options.GetPolicy(policyName));
    }
}

在上一章中介绍过,我们定义的策略都保存在《AuthorizationOptions》的字典中,因此在这里只是简单的将AuthorizationOptions中的同名方法异步化。

IAuthorizationHandlerContextFactory

授权上下文是我们接触较多的对象,当我们自定义授权Handler时就会用到它,它是使用简单工厂模式来创建的:

public class DefaultAuthorizationHandlerContextFactory : IAuthorizationHandlerContextFactory
{
    public virtual AuthorizationHandlerContext CreateContext(IEnumerable<IAuthorizationRequirement> requirements, ClaimsPrincipal user, object resource)
    {
        return new AuthorizationHandlerContext(requirements, user, resource);
    }
}

授权上下文中主要包含用户的Claims和授权策略的Requirements

public class AuthorizationHandlerContext
{
    private HashSet<IAuthorizationRequirement> _pendingRequirements;
    private bool _failCalled;
    private bool _succeedCalled;

    public AuthorizationHandlerContext(IEnumerable<IAuthorizationRequirement> requirements, ClaimsPrincipal user, object resource)
    {
        Requirements = requirements; User = user; Resource = resource;
        _pendingRequirements = new HashSet<IAuthorizationRequirement>(requirements);
    }

    public virtual bool HasFailed { get { return _failCalled; } }
    public virtual bool HasSucceeded => !_failCalled && _succeedCalled && !_pendingRequirements.Any();
    public virtual void Fail()
    {
        _failCalled = true;
    }
    public virtual void Succeed(IAuthorizationRequirement requirement)
    {
        _succeedCalled = true;
        _pendingRequirements.Remove(requirement);
    }
}

如上,_pendingRequirements中保存着所有待验证的Requirements,验证成功的Requirement则从中移除。

IAuthorizationHandlerProvider

兜兜转转,终于进入到了授权的最终验证逻辑中了,首先,使用IAuthorizationHandlerProvider来获取到所有的授权Handler

IAuthorizationHandlerProvider的默认实现为DefaultAuthorizationHandlerProvider:

public class DefaultAuthorizationHandlerProvider : IAuthorizationHandlerProvider
{
    private readonly IEnumerable<IAuthorizationHandler> _handlers;

    public DefaultAuthorizationHandlerProvider(IEnumerable<IAuthorizationHandler> handlers)
    {
        _handlers = handlers;
    }

    public Task<IEnumerable<IAuthorizationHandler>> GetHandlersAsync(AuthorizationHandlerContext context)
        => Task.FromResult(_handlers);
}

在《上一章》中,我们还介绍到,我们定义的Requirement,可以直接实现IAuthorizationHandler接口,也可以单独定义Handler,但是需要注册到DI系统中去。

在默认的AuthorizationHandlerProvider中,会从DI系统中获取到我们注册的所有Handler,最终调用其HandleAsync方法。

我们在实现IAuthorizationHandler接口时,通常是继承自AuthorizationHandler<TRequirement>来实现,它有如下定义:

public abstract class AuthorizationHandler<TRequirement> : IAuthorizationHandler where TRequirement : IAuthorizationRequirement
{
    public virtual async Task HandleAsync(AuthorizationHandlerContext context)
    {
        foreach (var req in context.Requirements.OfType<TRequirement>())
        {
            await HandleRequirementAsync(context, req);
        }
    }

    protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement);
}

如上,首先会在HandleAsync过滤出与Requirement对匹配的Handler,然后再调用其HandleRequirementAsync方法。

那我们定义的直接实现IAuthorizationHandler了接口的Requirement又是如何执行的呢?

AddAuthorization扩展方法中可以看到,默认还为IAuthorizationHandler注册了一个PassThroughAuthorizationHandler,定义如下:

public class PassThroughAuthorizationHandler : IAuthorizationHandler
{
    public async Task HandleAsync(AuthorizationHandlerContext context)
    {
        foreach (var handler in context.Requirements.OfType<IAuthorizationHandler>())
        {
            await handler.HandleAsync(context);
        }
    }
}

它负责调用该策略中所有实现了IAuthorizationHandler接口的Requirement

IAuthorizationEvaluator

最后,通过调用IAuthorizationEvaluator接口,来完成最终的授权结果,默认实现为DefaultAuthorizationEvaluator:

public class DefaultAuthorizationEvaluator : IAuthorizationEvaluator
{
    public AuthorizationResult Evaluate(AuthorizationHandlerContext context)
        => context.HasSucceeded
            ? AuthorizationResult.Success()
            : AuthorizationResult.Failed(context.HasFailed
                ? AuthorizationFailure.ExplicitFail()
                : AuthorizationFailure.Failed(context.PendingRequirements));
}

当我们在一个策略中指定多个Requirement时,只有全部验证通过时,授权上下文中的HasSucceeded才会为True,而HasFailed代表授权结果的显式失败。

这里根据授权上下文的验证结果来生成授权结果:

public class AuthorizationResult
{
    public bool Succeeded { get; private set; }
    public AuthorizationFailure Failure { get; private set; }
    public static AuthorizationResult Success() => new AuthorizationResult { Succeeded = true };
    public static AuthorizationResult Failed(AuthorizationFailure failure) => new AuthorizationResult { Failure = failure };
    public static AuthorizationResult Failed() => new AuthorizationResult { Failure = AuthorizationFailure.ExplicitFail() };
}

public class AuthorizationFailure
{
    private AuthorizationFailure() { }
    public bool FailCalled { get; private set; }
    public IEnumerable<IAuthorizationRequirement> FailedRequirements { get; private set; }
    public static AuthorizationFailure ExplicitFail()
    {
        return new AuthorizationFailure { FailCalled = true, FailedRequirements = new IAuthorizationRequirement[0] };
    }
    public static AuthorizationFailure Failed(IEnumerable<IAuthorizationRequirement> failed)
        => new AuthorizationFailure { FailedRequirements = failed };

}

整个授权流程的结构大致如下:

authorization_service

总结

通过对 ASP.NET Core 授权系统执行流程的探索,可以看出授权是主要是通过调用IAuthorizationService来完成的,而授权策略的本质是提供 Requirement ,我们完全可以使用它们两个来完成各种灵活的授权方式,而不用局限于策略。在 ASP.NET Core 中,还提供了基于资源的授权,放在《下一章》中来介绍,并会简单演示一下在一个通用权限管理系统中如何来授权。

ASP.Net Core下Authorization的几种方式_nic7968的博客-CSDN博客_.net authorization

mikel阅读(597)

来源: ASP.Net Core下Authorization的几种方式_nic7968的博客-CSDN博客_.net authorization

Authorization其目标就是验证Http请求能否通过验证。ASP.NET Core提供了很多种Authorization方式,详细可以参考 微软官方文档。在这里只详细介绍三种方式:

  • Policy
  • Middleware
  • Custom Attribute

1. Policy : 策略授权

先定义一个IAuthorizationRequirement类来定义策略的要求,以下例子支持传递一个age参数。

  1. public class AdultPolicyRequirement : IAuthorizationRequirement
  2. {
  3. public int Age { get; }
  4. public AdultPolicyRequirement(int age)
  5. {
  6. //年龄限制
  7. this.Age = age;
  8. }
  9. }

然后定义策略要求的Handler,当提供的Controller被请求时先根据请求的Http报文来决定是否可以通过验证。

  1. public class AdultAuthorizationHandler : AuthorizationHandler
  2. {
  3. protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AdultPolicyRequirement requirement)
  4. {
  5. //获取当前http请求的context对象
  6. var mvcContext = context.Resource as AuthorizationFilterContext;
  7. //以下代码都不是必须的,只是展示一些使用方法,你可以选择使用
  8. ……
  9. //
  10. var age = mvcContext.HttpContext.Request.Query.FirstOrDefault(u => u.Key == “age”);
  11. if (age.Value.Count <= 0|| Convert.ToInt16(age.Value[0]) < requirement.Age)
  12. {
  13. context.Fail();
  14. }
  15. else
  16. {
  17. //通过验证,这句代码必须要有
  18. context.Succeed(requirement);
  19. }
  20. return Task.CompletedTask;
  21. }
  22. }

还需要在启动时,在services里注册定义的策略和对应的Handler

  1. //添加二种认证策略,一种以12岁为界限,一种是18岁
  2. services.AddAuthorization(options =>
  3. {
  4. options.AddPolicy(“Adult1”, policy =>
  5. policy.Requirements.Add(new AdultPolicyRequirement(12)));
  6. options.AddPolicy(“Adult2”, policy =>
  7. policy.Requirements.Add(new AdultPolicyRequirement(18)));
  8. });
  9. //添加策略验证handler
  10. services.AddSingleton();

最后在相应的Controller前加上Authroize特性 [Authorize("Adult1")]。总体上Policy这种方式比较简单,但是也有不灵活的地方,不同的策略要求都需要提前在services里注册。完整的例子可以参考 PolicySample

2. Middleware: 中间件方式

这种方式并不是专门用于授权,它的用途更广更灵活,它用于在所有Http Request和Response前授权检查、数据处理、错误跳转、日志处理等。详细说明可以参考 官方Middleware说明

image

这个图是基本的结构和运行图,我们可以创建多个中间组件,组成一个管道,在Http的Request发出和Response创建之前对HttpContext.Request和HttpContext.Response进行处理。看以下例子,定义了2个中间组件类:

  1. public class AuthorizeMiddleware
  2. {
  3. private readonly RequestDelegate next;
  4. public AuthorizeMiddleware(RequestDelegate next)
  5. {
  6. this.next = next;
  7. }
  8. public async Task Invoke(HttpContext context /* other scoped dependencies */)
  9. {
  10. //以下代码都不是必须的,只是展示一些使用方法,你可以选择使用
  11. //这个例子只是修改一下response的header
  12. context.Response.OnStarting(state => {
  13. var httpContext = (HttpContext)state;
  14. httpContext.Response.Headers.Add(“test2”, “testvalue2”);
  15. return Task.FromResult(0);
  16. }, context);
  17. //处理结束转其它中间组件去处理
  18. await next(context);
  19. }
  20. }
  1. public class OtherMiddleware
  2. {
  3. private readonly RequestDelegate next;
  4. public OtherMiddleware(RequestDelegate next)
  5. {
  6. this.next = next;
  7. }
  8. public async Task Invoke(HttpContext context )
  9. {
  10. //这个例子只是修改一下response的header
  11. context.Response.OnStarting(state => {
  12. var httpContext = (HttpContext)state;
  13. httpContext.Response.Headers.Add(“test1”, “testvalue1” );
  14. return Task.FromResult(0);
  15. }, context);
  16. await next(context);
  17. }
  18. }

这里定义的类不需要实现接口或集成系统的类。只需要给app增加middleware代理类的定义。注意在HttpResponse发出之后就不要再调用next.invoke()。以下三句代码的执行顺序不能弄错。

  1. app.UseMiddleware(typeof(AuthorizeMiddleware));
  2. app.UseMiddleware(typeof(OtherMiddleware));
  3. app.UseMvc();

执行web请求后执行的顺序是:

  • 执行AuthorizeMiddleware的invoke方法
  • 执行OtherMiddleware的invoke方法
  • 执行ValueController的Get方法
  • 执行OtherMiddleware的修改header方法
  • 执行AuthorizeMiddleware的修改header方法
  • 发出Http Response

大家可以自己执行一下代码来理解。代码参考 Github地址

3. Custom Attribute:自定义特性

这里其实是第一种Policy策略和自定义特性的结合,从而实现在Controller的具体方法位置自定义不同参数的Policy策略。

首先需要定义这个Attribute和策略要求类

  1. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
  2. public class PermissionCheckAttribute : AuthorizeAttribute
  3. {
  4. public string Id { get; set; }
  5. public int Operation { get; set; }
  6. public PermissionCheckAttribute() : base(“PermissionCheck”)
  7. {
  8. }
  9. }
  10. public class PermissionCheckPolicyRequirement : IAuthorizationRequirement
  11. {
  12. //Add any custom requirement properties if you have them
  13. public PermissionCheckPolicyRequirement()
  14. {
  15. }
  16. }

再定义策略和Attribute对应的Handler处理类

  1. public class PermissionCheckPolicyHandler : AttributeAuthorizationHandler<PermissionCheckPolicyRequirement, PermissionCheckAttribute>
  2. {
  3. protected override Task HandleRequirementAsync(AuthorizationHandlerContext authoriazationContext,
  4. PermissionCheckPolicyRequirement requirement, IEnumerable<PermissionCheckAttribute> attributes)
  5. {
  6. var context = authoriazationContext.Resource as AuthorizationFilterContext;
  7. foreach (var permissionAttribute in attributes)
  8. {
  9. this.checkPermission(context, permissionAttribute.Id, permissionAttribute.Operation);
  10. }
  11. authoriazationContext.Succeed(requirement);
  12. return Task.FromResult<object>(null);
  13. }
  14. private void checkPermission(AuthorizationFilterContext context, string _Id, int _Operation)
  15. {
  16. if (_Operation > 0)
  17. {
  18. if (_Id != “user1”)
  19. {
  20. throw new Exception(“不具备操作权限”);
  21. }
  22. }
  23. else
  24. {
  25. //dosomething
  26. }
  27. return;
  28. }
  29. }

同样还需要在service里添加策略和策略处理类,这里不贴代码了。最后在Controller里使用带参数的Attribute,类似如下:

  1. [HttpGet]
  2. [PermissionCheck (Id =“user1”, Operation=2)]
  3. public IEnumerable Get()
  4. {
  5. return new string[] { “value1”, “value2” };
  6. }
  7. // GET api/values/5
  8. [HttpGet(“{id}”)]
  9. [PermissionCheck(Id = “user2”, Operation = 4)]
  10. public string Get(int id)
  11. {
  12. return “value”;
  13. }

完整的代码参考Github地址

作者:voxer
链接:https://www.jianshu.com/p/0ed4d820809c

探索 .NET Core 依赖注入的 IServiceCollection - 腾讯云开发者社区-腾讯云

mikel阅读(580)

来源: 探索 .NET Core 依赖注入的 IServiceCollection – 腾讯云开发者社区-腾讯云

如果您使用了.NET Core,则很可能已使用Microsoft.Extensions.DependencyInjection中的内置依赖项注入容器,在本文中,我想更深入地了解Microsoft Dependency Injection(DI)容器中的 IServiceCollection。

什么是依赖注入(DI)和DI容器?

Microsoft依赖项注入容器只是一组类,它们组合到一个代码库中,这个库会自动创建并管理程序中需要的对象。

我们先看下面的代码:

public class ClassA
{
    public void DoWork() 
    {
        var b = new ClassB();
        b.DoStuff();
    }
}

public class ClassB
{
    public void DoStuff()
    {
        // ...
    }
}

ClassA直接依赖ClassB,并且在它的DoWork方法中,new了一个ClassB,然后调用了ClassB的DoStuff方法。

我们改写一下代码看看:

public class ClassA
{
    private readonly ClassB _dependency;

    public ClassA(ClassB classB) => _dependency = classB;

    public void DoWork() => _dependency.DoStuff();
}

public class ClassB : IThing
{
    public void DoStuff()
    {
        // ...
    }
}

首先,我加了一个构造函数,并且指定了ClassA依赖的类型,调用构造函数时,必须提供ClassB的实例, 在ClassA的内部,我们不会去new一个ClassB,ClassB完全是由外部传入的,这里就是控制反转(IoC)。

进一步改进代码:

public interface IThing
{
    public void DoStuff();
}

public class ClassA
{
    private readonly IThing _dependency;

    public ClassA(IThing thing) => _dependency = thing;

    public void DoWork() => _dependency.DoStuff();
}

public class ClassB : IThing
{
    public void DoStuff()
    {
        // ...
    }
}

加了一个接口IThing,现在,我们已经应用了SOLID的依赖倒置原则,我们不再依赖具体的实现,相反,我们依赖于IThing抽象,在构造函数中,只需要传入IThing的实现就好了。

然后在我们的代码中,可以这样用:

class Program
{
    static void Main(string[] args)
    {
        IThing thing = new ClassB();
        ClassA classA = new ClassA(thing);
        classA.DoWork();
    }
}

我们手动new了一个ClassB,它实现了IThing接口,然后创建ClassA的时候,直接把thing传入构造函数中。

上面的代码演示,我们只处理了ClassA和ClassB的依赖注入关系,但是在实际中呢,我们代码中有很多类型,然后有各种各样的依赖关系。

这个时候我们就需要一个DI容器,我们对容器进行配置,然它知道什么类型,然后负责自动创建并管理对象(通常称为服务)。

注册服务

通常, Microsoft DI 容器需要在Startup类中配置,在这里,您可以使用ConfigureServices方法向容器注册服务,在应用程序托管生命周期的早期,将调用ConfigureServices方法,它有一个参数IServiceCollection,这个参数在初始化应用程序时传入。

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // 注册服务
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
    }
}

为了尽可能的简单,我们也可以在控制台中使用 Microsoft DependencyInjection。

创建控制台程序后,我们首先在项目中引入Microsoft.Extensions.DependencyInjection

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
  </ItemGroup>

</Project>

现在我们开始注册我们的服务,但是我们需要一个IServiceCollection,让我们看一下IServiceCollection的定义。

public interface IServiceCollection : IList<ServiceDescriptor>
{
}

IServiceCollection没有定义其任何成员,而是从IList<ServiceDescriptor>派生。

Microsoft.Extensions.DepenencyInjection程序包里面,它有一个默认的实现:ServiceCollection。

public class ServiceCollection : IServiceCollection
{
    private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();

    public int Count => _descriptors.Count;

    public bool IsReadOnly => false;

    public ServiceDescriptor this[int index]
    {
        get
        {
            return _descriptors[index];
        }
        set
        {
            _descriptors[index] = value;
        }
    }

    // ...
}

它有一个私有的List集合:_descriptors,里面是ServiceDescriptor。

让我们从创建一个ServiceCollection,然后注册两个服务。

static void Main(string[] args)
{
    var serviceCollection = new ServiceCollection();

    serviceCollection.AddSingleton<ClassA>();
    serviceCollection.AddSingleton<IThing, ClassB>();

    Console.WriteLine("Done");
}

在前面的代码中,我们已经使用AddSingleton方法注册了两个服务,这不是IServiceCollection接口定义的方法,也不在ServiceCollection上,这是IServiceCollection的扩展方法,这个方法在ServiceCollectionServiceExtensions的扩展类中,接下来,我会介绍这个方法是如何注册服务的,不过这之前,我们首先回顾下服务生命周期的概念。

服务生命周期

在Microsoft依赖项注入框架中,我们可以使用三种生命周期注册服务,分别是单例(Singleton)、瞬时(Transient)、作用域(Scoped),在上面的代码中,我使用了AddSingleton()来注册服务。

使用Singleton服务的优点是我们不会创建多个服务实例,只会创建一个实例,保存到DI容器中,直到程序退出,这不仅效率高,而且性能高,但是有一个要注意的点,如果在多线程中使用了Singleton,要考虑线程安全的问题,保证它不会有冲突。

瞬时(Transient)和单例(Singleton)模式是相反的,每次使用时,DI容器都是创建一个新的实例。

作用域(Scoped),在一个作用域内,会使用同一个实例,像EF Core的DbContext上下文就被注册为作用域服务。

我们注册服务时会发生什么?

在上面的代码中,我已经注册了两个单例服务。

serviceCollection.AddSingleton<ClassA>();
serviceCollection.AddSingleton<IThing, ClassB>();

这是最终的AddSingleton方法:

public static IServiceCollection AddSingleton(
    this IServiceCollection services,
    Type serviceType,
    Type implementationType)
{
    if (services == null)
    {
        throw new ArgumentNullException(nameof(services));
    }
    if (serviceType == null)
    {
        throw new ArgumentNullException(nameof(serviceType));
    }
    if (implementationType == null)
    {
        throw new ArgumentNullException(nameof(implementationType));
    }
    return Add(services, serviceType, implementationType, ServiceLifetime.Singleton);
}

我们可以看到AddSingleton方法调用了私有的Add方法,并且传入了一个生命周期的枚举值ServiceLifetime.Singleton

让我们看一下Add方法的工作原理:

private static IServiceCollection Add(
    IServiceCollection collection,
    Type serviceType,
    Type implementationType,
    ServiceLifetime lifetime)
{
    var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);
    collection.Add(descriptor);
    return collection;
}

它创建一个新的ServiceDescriptor实例,传入服务类型,实现类型(可能与服务类型相同)和生命周期,然后调用Add方法添加到列表中。

之前,我们了解到IServiceCollection本质上是包装了List <ServiceDescriptor>, ServiceDescriptor类很简单,代表一个注册的服务,包括其服务类型,实现类型和生命周期。

实例注册

我们也可以手动new一个实例,然后传入到AddSingleton()方法中:

var myInstance = new ClassB();
serviceCollection.AddSingleton<IThing>(myInstance);

使用 ServiceDescriptor

我们还可以手动定义一个ServiceDescriptor,然后直接添加到IServiceCollection中。

var descriptor = new ServiceDescriptor(typeof(IThing), typeof(ClassB), ServiceLifetime.Singleton);
serviceCollection.Add(descriptor);

总结

在本文中,介绍了.NET中的DI的一些核心知识,可以直接创建ServiceCollection来使用Microsoft DI框架,了解了IServiceCollection上的AddSingleton扩展方法是如何工作,以及它们最终创建了一个ServiceDescriptor,然后添加到一个ServiceCollection包装的List集合中。

原文链接: https://www.stevejgordon.co.uk/aspnet-core-dependency-injection-what-is-the-iservicecollection