Asp.net core Identity + identity server + angular 学习笔记 (第四篇)
来说说 RBAC (role based access control)
这是目前全世界最通用的权限管理机制, 当然使用率高并不是说它最好. 它也有很多局限的.
我们来讲讲最简单的 role based 概念.
想象一间公司开始的时候,只有一个管理人.
一个 application 一个 user, 只要登入就可以了.
后来业务大了,就要请人来分工,我们不可能简单的开多一个 user 给新人,因为新人只需要做一部分的工作而且外人是不可靠的.
所以我们需要有限权.
一般上的做法是从 task 出发. 我们把所有工作切分成多个 task (比如,管理订单, 管理产品, 管理订货, 管理钱..等等)
一般上一个 task 会有一个对应的菜单和 ui 操作页面, 页面里就涉及多个表的 CRUD 等等操作了。
分工的概念就是, 一个 user 可以负责多个 task. 比如我要你去管订单和订货。
但是任务多起来就不好管理了,所以我们又往上创建一个角色或者职位的概念. 可能是叫经理, 客服, 采购员等等.
最后变成了 : 一个 user 对应多个 role 对应多个 task 对应多个 operation
上面只是一个基本概念, 核心思想就是角色与分工.
很多人也对上面的方式进行了扩展,比如角色继承 (老板用于所有经理得权限)
再比如角色组, 我可以把权限设置给组, 那么里头得角色都拥有这个权限了.
这些扩展都是为了更方便的去收发权限.
动态创建角色 ?
有些系统可以让用户自己去创建角色, 然后配置给用户.
这种分离适合用于那种卖软件的形式. 比如同一个软件卖给1000间不同规模的公司.
系统在开发的时候,完全没有 role 的概念, 因为 role 必须是后来用户自己创建的.
系统只按 module 或者说 task 来切分, 切的越细腻,未来授权就可以越细,是一种提前设计的方式.
系统发布后, user 自行创建更多的 role 然后支配 task 来做权限管理.
这种方式比较适合用于内部人员的管理授权.
rbac 的不足
权限比我们想象的复杂, 大部分情况下,我们是通过拦截用户的 http 请求来限制用户的访问.
也就是说,要嘛可以,要嘛不可以.
但现实情况下还有很多一半一半的情况. 比如, 你是销售经理, 但是只负责 A 区, 所以你只可以看见 A 区的客户资料和销售报告.
这个在代码里,是要通过 sql where 来实现的. 根本不是简单的能访问 api 还是不行.
于是就有了 abac (attribute base access control)
区域就是一个 attribute 一个变量, 系统在设计之初就要把这个写入 where 语句中. 不然也无法实现.
它们之间的关系是这样的 某个 task 有分区的概念. 那么在授权这个 task 时也必须清楚表明授权的区域. 我把这个称为 task 变量
下一篇我们才来说细节. 先看看 rbac 的实现.
首先是修改 startup service
// 替换
//services.AddAuthentication(o =>
//{
// o.DefaultScheme = IdentityConstants.ApplicationScheme;
// o.DefaultSignInScheme = IdentityConstants.ExternalScheme;
//})
//.AddIdentityCookies(o => { });
//services.AddIdentityCore<IdentityUser>(o =>
//{
// o.Stores.MaxLengthForKeys = 128;
//}) services.AddIdentity<IdentityUser, IdentityRole>(o =>
{
o.Stores.MaxLengthForKeys = ;
})
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager()
.AddRoles<IdentityRole>(); // 加这个
我们把 AddIdentityCore 换掉了, 加了一个 AddRoles
这里来看一看源码 AddDefaultIdentity vs AddIdentityCore vs AddIdentity
AddDefaultIdentity
AddIdentityCore
AddIdentity
public static IdentityBuilder AddIdentity<TUser, TRole>(
this IServiceCollection services,
Action<IdentityOptions> setupAction)
where TUser : class
where TRole : class
{
// Services used by identity
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddCookie(IdentityConstants.ApplicationScheme, o =>
{
o.LoginPath = new PathString("/Account/Login");
o.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
};
})
.AddCookie(IdentityConstants.ExternalScheme, o =>
{
o.Cookie.Name = IdentityConstants.ExternalScheme;
o.ExpireTimeSpan = TimeSpan.FromMinutes();
})
.AddCookie(IdentityConstants.TwoFactorRememberMeScheme, o =>
{
o.Cookie.Name = IdentityConstants.TwoFactorRememberMeScheme;
o.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = SecurityStampValidator.ValidateAsync<ITwoFactorSecurityStampValidator>
};
})
.AddCookie(IdentityConstants.TwoFactorUserIdScheme, o =>
{
o.Cookie.Name = IdentityConstants.TwoFactorUserIdScheme;
o.ExpireTimeSpan = TimeSpan.FromMinutes();
}); // Hosting doesn't add IHttpContextAccessor by default
services.AddHttpContextAccessor();
// Identity services
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
// No interface for the error describer so we can add errors without rev'ing the interface
services.TryAddScoped<IdentityErrorDescriber>();
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
services.TryAddScoped<ITwoFactorSecurityStampValidator, TwoFactorSecurityStampValidator<TUser>>();
services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
services.TryAddScoped<UserManager<TUser>>();
services.TryAddScoped<SignInManager<TUser>>();
services.TryAddScoped<RoleManager<TRole>>(); if (setupAction != null)
{
services.Configure(setupAction);
} return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
}
要完整的话,还是 addIdentity 比较齐全.
来看看数据结构
identity 的结构是这样的, 一个 user 对应多个 role 对应多个 claim
上面我们说到 "最后变成了 : 一个 user 对应多个 role 对应多个 task 对应多个 operation"
这和我们上面说的不太对应啊. 没错. 是对不上的.
[Authorize(Roles = "Admin")]
public class ContactModel : PageModel
这个是 identity 教程中如何使用 role 来保护页面的做法.
而我上面说的应该是这样的表达才对
[Authorize(Task = "ManageContact")]
public class ContactModel : PageModel
操作基于 task 来区分, role 绑定 task, 这样才能灵活替换 role 和 task. 依据 identity 的做法, 如果我想把某个工作分给另一个 role 就得修改代码了。
那 claim 又是啥呢 ? claim 就是 abac 的属性. 我们下一篇要讲的东西,先不管.
下边是一些基本得操作, create , get all, add claims, add role to user, 就不一一写到完了
public async Task OnPostCreateRole(
[FromServices] RoleManager<IdentityRole> roleManager,
[FromServices] UserManager<IdentityUser> userManager
)
{
var role = new IdentityRole { Name = "Manager" };
var result = await roleManager.CreateAsync(new IdentityRole { Name = "Manager" });
if (result.Succeeded)
{
var allRoles = await roleManager.Roles.ToListAsync();
await roleManager.AddClaimAsync(role, new Claim("type", "value"));
var user = await userManager.FindByNameAsync("hengkeat87@gmail.com");
await userManager.AddToRoleAsync(user, "Manager");
}
}
有了 role, 我们就可以通过这样来保护 page, controller and action 了.
[Authorize(Roles = "Admin,Manager")] //admin or manager
public class ContactModel : PageModel
虽然这个不够灵活但是在小项目里,已经基本够用了.
Asp.net core Identity + identity server + angular 学习笔记 (第四篇)的更多相关文章
- Asp.net core Identity + identity server + angular 学习笔记 (第三篇)
register -> login 讲了 我们来讲讲 forgot password -> reset password 和 change password 吧 先来 forgot pa ...
- Asp.net core Identity + identity server + angular 学习笔记 (第五篇)
ABAC (Attribute Based Access Control) 基于属性得权限管理. 属性就是 key and value 表达力非常得强. 我们可以用 key = role value ...
- Asp.net core Identity + identity server + angular 学习笔记 (第一篇)
用了很长一段时间了, 但是一直没有做过任何笔记,感觉 identity 太多东西要写了, 提不起劲. 但是时间一久很多东西都记不清了. 还是写一轮吧. 加深记忆. 这是 0-1 的笔记, 会写好多篇. ...
- Asp.net core Identity + identity server + angular 学习笔记 (第二篇)
先纠正一下第一篇的的错误. 在 Login.cshtml 和 Login.cshtml.cs 里, 本来应该是 Register 我却写成 Login . cshtml 修改部分 <form a ...
- ASP.NET Core 学习笔记 第四篇 ASP.NET Core 中的配置
前言 说道配置文件,基本大多数软件为了扩展性.灵活性都会涉及到配置文件,比如之前常见的app.config和web.config.然后再说.NET Core,很多都发生了变化.总体的来说技术在进步,新 ...
- Asp.Net Core + Dapper + Repository 模式 + TDD 学习笔记
0x00 前言 之前一直使用的是 EF ,做了一个简单的小项目后发现 EF 的表现并不是很好,就比如联表查询,因为现在的 EF Core 也没有啥好用的分析工具,所以也不知道该怎么写 Linq 生成出 ...
- ASP.NET Core微服务 on K8S学习笔记(第一章:详解基本对象及服务发现)
课程链接:http://video.jessetalk.cn/course/explore 良心课程,大家一起来学习哈! 任务1:课程介绍 任务2:Labels and Selectors 所有资源对 ...
- 【转】angular学习笔记(十四)-$watch(1)
本篇主要介绍$watch的基本概念: $watch是所有控制器的$scope中内置的方法: $scope.$watch(watchObj,watchCallback,ifDeep) watchObj: ...
- angular学习笔记(十四)-$watch(1)
本篇主要介绍$watch的基本概念: $watch是所有控制器的$scope中内置的方法: $scope.$watch(watchObj,watchCallback,ifDeep) watchObj: ...
随机推荐
- Elasticsearch.安装插件(head)
Elasticsearch.安装插件(head) 环境: Linux 7.x jdk1.8 目录结构(跟目录多了两个文件) /resources ### 存放软件源 /u01/ ...
- HTTP 返回状态代码
一.HTTP状态码 如果某项请求发送到您的服务器要求显示您网站上的某个网页(例如,用户通过浏览器访问您的网页或 Googlebot 抓取网页时),服务器将会返回 HTTP 状态代码以响应请求. 此状态 ...
- 爬取豆瓣电影影评,生成wordcloud词云,并利用监督学习根据评论自动打星
本文的完整源码在git位置:https://github.com/OceanBBBBbb/douban-ml 爬取豆瓣影评 爬豆瓣的影评比较简单,豆瓣没有做限制,甚至你都不用登陆就可以看全部,我这里用 ...
- Java 动态打印菱形代码之for循环的使用
1.自定义空心菱形 void PrintRhombus() { int i, j; int s = 4; for (i = 1; i < 2 * (s + 1); i++) { if (i &l ...
- P1772 [ZJOI2006]物流运输
题目描述 物流公司要把一批货物从码头A运到码头B.由于货物量比较大,需要n天才能运完.货物运输过程中一般要转停好几个码头.物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪. ...
- what?iView的DropDown没有element的split-button?提issure?等不及了,自己实现一个
开始正文之前,有必要先说自己实现这个组件的必要性描述. 话说大家做表格时,增删查改按钮都是放在哪里的?最简单的方式应该是这样: 是不是感觉奇丑无比啊,于是改成了这样: 但是这种操作按钮一多后就没位置放 ...
- [Atcoder SoundHound Contest 2018]E.+ Graph
题面 Time limit : 2sec / Memory limit : 1024MB Score : 600 points Problem Statement-题目描述 Kenkoooo foun ...
- redis 在 php 中的应用(key篇)
本文为我阅读了 redis参考手册 之后结合 博友的博客 编写,注意 php_redis 和 redis-cli 的区别(主要是返回值类型和参数用法) 目录: KEY(键) DEL ...
- Python-----多线程threading用法
threading模块是Python里面常用的线程模块,多线程处理任务对于提升效率非常重要,先说一下线程和进程的各种区别,如图 概括起来就是 IO密集型(不用CPU) 多线程计算密集型(用CPU) 多 ...
- Java8-对map过滤
1.对map按值过滤返回值 public class TestMapFilter { public static void main(String[] args) { Map<Integer, ...