上一章分享了如何在ASP.NET Core中应用JWT进行用户认证以及Token的刷新,本章继续进行下一步,用户授权。涉及到的例子也以上一章的为基础。(ASP.NET Core 系列目录)

一、概述

  首先说一下认证(authentication)与授权(authorization),它们经常在一起工作,所以有时候会分不清楚。并且这两个英文单词长得也像兄弟。举例来说,我刷门禁卡进入公司,门禁【认证】了我是这里的员工,可以进入;但进入公司以后,我并不是所有房间都可以进,比如“机房重地,闲人免进”,我能进入哪些房间,需要公司的【授权】。这就是认证和授权的区别。

  ASP.NET Core提倡的是基于声明(Claim)的授权,关于这个Claim,上一章用到过,有如下这样的代码,但没有介绍:

Claim[] claims = new Claim[] { new Claim(ClaimTypes.NameIdentifier, user.Code), new Claim(ClaimTypes.Name, user.Name) };

这是一个声明的集合,它包含了两个 声明,用于保存了用户的唯一ID和用户名。当然我们还可以添加更多的Claim。对应Claim,还有ClaimsIdentity 和ClaimsPrincipal 两个类型。

ClaimsIdentity相当于是一个证件,例如上例的门禁卡;ClaimsPrincipal 则是证件的持有者,也就是我本人;那么对应的Claim就是门禁卡内存储的一些信息,例如证件号、持有人姓名等。

我除了门禁卡还有身份证、银行卡等,也就是说一个ClaimsPrincipal中可以有多个ClaimsIdentity,而一个ClaimsIdentity中可以有多个Claim。ASP.NET Core的授权模型大概就是这样的一个体系。

ASP.NET Core支持多种授权方式,包括兼容之前的角色授权。下面通过几个例子说明一下(例子依然以上一章的代码为基础)。

二、基于角色授权

  ASP.NET Core兼容之前的角色授权模式,如何使用呢?由于不是本文的重点,这里只是简要说一下。修改FlyLolo.JWT.Server的TokenHelper临时为张三添加了一个名为“TestPutBookRole”的权限(实际权限来源此处不做展示)。

        public ComplexToken CreateToken(User user)
{
Claim[] claims = new Claim[] { new Claim(ClaimTypes.NameIdentifier, user.Code), new Claim(ClaimTypes.Name, user.Name) }; //下面对code为001的张三添加了一个Claim,用于测试在Token中存储用户的角色信息,对应测试在FlyLolo.JWT.API的BookController的Put方法,若用不到可删除
if (user.Code.Equals(""))
{
claims = claims.Append(new Claim(ClaimTypes.Role, "TestPutBookRole")).ToArray();
} return CreateToken(claims);
}

修改FlyLolo.JWT.API的BookController,添加了一个Action如下

        /// <summary>
/// 测试在JWT的token中添加角色,在此验证 见TokenHelper
/// </summary>
/// <returns></returns>
[HttpPut]
[Authorize(Roles = "TestPutBookRole")]
public JsonResult Put()
{
return new JsonResult("Put Book ...");
}

访问这个Action,只有用张三登录后获取的Token能正常访问。

三、基于声明授权

对于上例来说,本质上也是基于声明(Claim)的授权,因为张三的"TestPutBookRole"角色也是作为一个Claim添加到证书中的。只不过采用了特定的ClaimTypes.Role。那么是否可以将其他的普通Claim作为授权的依据呢?当然是可以的。

这里涉及到了另一个单词“Policy”,翻译为策略?也就是说,可以把一系列的规则(例如要求姓名为李四,账号为002,国籍为中国等等)组合在一起,形成一个Policy,只有满足这个Policy的才可以被授权访问。

下面我们就新建一个Policy,在Startup的ConfigureServices中添加授权代码:

services.AddAuthorization(options=>options.AddPolicy("Name",policy=> {
policy.RequireClaim(ClaimTypes.Name, "张三");
policy.RequireClaim(ClaimTypes.NameIdentifier,"");
}));

在BookController中添加一个Action如下

[HttpDelete]
[Authorize(Policy = "TestPolicy")]
public JsonResult Delete()
{
return new JsonResult("Delete Book ...");
}

可以通过张三和李四的账号测试一下,只有使用张三的账号获取的Token能访问成功。

四、基于策略自定义授权

上面介绍了两种授权方式,现在有个疑问,通过角色授权,只适合一些小型项目,将几个功能通过角色区分开就可以了。

通过声明的方式,目测实际项目中需要在Startup中先声明一系列的Policy,然后在Controller或Action中使用。

这两种方式都感觉不好。例如经常存在这样的需求:一个用户可以有多个角色,每个角色对应多个可访问的API地址(将授权细化到具体的Action)。用户还可以被特殊的授予某个API地址的权限。

这样的需求采用上面的两种方式实现起来都很麻烦,好在ASP.NET Core提供了方便的扩展方式。

1.样例数据

将上面的需求汇总一下,最终可以形成如下形式的数据:

/// <summary>
/// 虚拟数据,模拟从数据库或缓存中读取用户相关的权限
/// </summary>
public static class TemporaryData
{
public readonly static List<UserPermissions> UserPermissions = new List<UserPermissions> {
new UserPermissions {
Code = "",
Permissions = new List<Permission> {
new Permission { Code = "A1", Name = "student.create", Url = "/api/student",Method="post" },
new Permission { Code = "A2", Name = "student.delete", Url = "/api/student",Method="delete"}
}
},
new UserPermissions {
Code = "",
Permissions = new List<Permission> {
new Permission { Code = "B1", Name = "book.create", Url = "/api/book" ,Method="post"},
new Permission { Code = "B2", Name = "book.delete", Url = "/api/book" ,Method="delete"}
}
},
}; public static UserPermissions GetUserPermission(string code)
{
return UserPermissions.FirstOrDefault(m => m.Code.Equals(code));
}
}

涉及到的两个类如下:

    public class Permission
{
public string Code { get; set; }
public string Name { get; set; }
public string Url { get; set; } public string Method { get; set; }
} public class UserPermissions
{
public string Code { get; set; }
public List<Permission> Permissions { get; set; }
}

2.自定义处理程序

下面就是根据样例数据来制定相应的处理程序了。这涉及到IAuthorizationRequirement和AuthorizationHandler两个内容。

IAuthorizationRequirement是一个空的接口,主要用于提供授权所需要满足的“要求”,或者说是“规则”。AuthorizationHandler则是对请求和“要求”的联合处理。

新建一个PermissionRequirement实现IAuthorizationRequirement接口。

public class PermissionRequirement: IAuthorizationRequirement
{
public List<UserPermissions> UsePermissionList { get { return TemporaryData.UserPermissions; } }
}

很简单的内容。它的“要求”也就是用户的权限列表了,用户的权限列表中包含当前访问的API,则授权通过,否则不通过。

判断逻辑放在新建的PermissionHandler中:

public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
var code = context.User.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.NameIdentifier));
if (null != code)
{
UserPermissions userPermissions = requirement.UsePermissionList.FirstOrDefault(m => m.Code.Equals(code.Value.ToString())); var Request = (context.Resource as AuthorizationFilterContext).HttpContext.Request; if (null != userPermissions && userPermissions.Permissions.Any(m => m.Url.ToLower().Equals(Request.Path.Value.ToLower()) && m.Method.ToLower().Equals(Request.Method.ToLower()) ))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
}
else
{
context.Fail();
} return Task.CompletedTask;
}
}

逻辑很简单不再描述。

3.使用自定义的处理程序

在Startup的ConfigureServices中添加授权代码

services.AddAuthorization(options => options.AddPolicy("Permission", policy => policy.Requirements.Add(new PermissionRequirement())));
services.AddSingleton<IAuthorizationHandler, PermissionHandler>();

将BookController的Delete Action修改一下:

[HttpDelete]
//[Authorize(Policy = "TestPolicy")]
[Authorize(Policy = "Permission")]
public JsonResult Delete()
{
return new JsonResult("Delete Book ...");
}

测试一下只有李四可以访问这个Action。

代码地址:https://github.com/FlyLolo/JWT.Demo/releases/tag/2.0

ASP.NET Core 2.2 : 二十七. JWT与用户授权(细化到Action)的更多相关文章

  1. ASP.NET Core 2.2 : 二十六. 应用JWT进行用户认证

    本文将通过实际的例子来演示如何在ASP.NET Core中应用JWT进行用户认证以及Token的刷新方案(ASP.NET Core 系列目录) 一.什么是JWT? JWT(json web token ...

  2. ASP.NET Core 2.2 : 二十六. 应用JWT进行用户认证及Token的刷新

    来源:https://www.cnblogs.com/FlyLolo/p/ASPNETCore2_26.html 本文将通过实际的例子来演示如何在ASP.NET Core中应用JWT进行用户认证以及T ...

  3. 学习ASP.NET Core(05)-使用Swagger与Jwt授权

    上一篇我们使用IOC容器解决了依赖问题,同时简单配置了WebApi环境,本章我们使用一下Swagger,并通过Jwt完成授权 一.Swagger的使用 1.什么是Swagger 前后端分离项目中,后端 ...

  4. ASP.NET Core学习之二 菜鸟踩坑

    对于像我这样没接触过core的人,坑还是比较多的,一些基础配置和以前差别很大,这里做下记录 一.Startup 1.注册服务 // This method gets called by the run ...

  5. 学习ASP.NET Core Razor 编程系列十七——分组

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

  6. ASP.NET Core 2.2 : 二十. Action的多数据返回格式处理机制

    上一章讲了系统如何将客户端提交的请求数据格式化处理成我们想要的格式并绑定到对应的参数,本章讲一下它的“逆过程”,如何将请求结果按照客户端想要的格式返回去. 一.常见的返回类型 以系统模板默认生成的Ho ...

  7. ASP.NET Core 2.2 : 二十一. 内容协商与自定义IActionResult和格式化类

    上一章的结尾留下了一个问题:同样是ObjectResult,在执行的时候又是如何被转换成string和JSON两种格式的呢? 本章来解答这个问题,这里涉及到一个名词:“内容协商”.除了这个,本章将通过 ...

  8. ASP.NET Core 2.2 : 二十二. 多样性的配置方式

    大多数应用都离不开配置,本章将介绍ASP.NET Core中常见的几种配置方式及系统内部实现的机制. 说到配置,第一印象可能就是“.config”类型的xml文件或者“.ini”类型的ini文件,在A ...

  9. ASP.NET Core 2.2 : 二十三. 深入聊一聊配置的内部处理机制

    上一章介绍了配置的多种数据源被注册.加载和获取的过程,本节看一下这个过程系统是如何实现的.(ASP.NET Core 系列目录) 一.数据源的注册 在上一节介绍的数据源设置中,appsettings. ...

随机推荐

  1. Python 之父撰文回忆:为什么要创造 pgen 解析器?

    花下猫语: 近日,Python 之父在 Medium 上开通了博客,并发布了一篇关于 PEG 解析器的文章(参见我翻的 全文译文).据我所知,他有自己的博客,为什么还会跑去 Medium 上写文呢?好 ...

  2. Java基础之十五 泛型

    第十五章 泛型 一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大. 在面对对象编程语言中,多态算是一种泛化机 ...

  3. bit、byte、kb、mb、g的区别

    1Byte=8bit1KB=1024Byte(字节)=8*1024bit1MB=1024KB1GB=1024MB1TB=1024GB bit是计算机数据的最小单元.要么是0,要么是1. byte 关键 ...

  4. RocketMQ中Broker的启动源码分析(二)

    接着上一篇博客  [RocketMQ中Broker的启动源码分析(一)] 在完成准备工作后,调用start方法: public static BrokerController start(Broker ...

  5. Hadoop 系列(二)—— 集群资源管理器 YARN

    一.hadoop yarn 简介 Apache YARN (Yet Another Resource Negotiator) 是 hadoop 2.0 引入的集群资源管理系统.用户可以将各种服务框架部 ...

  6. Java匹马行天下之J2EE框架开发——Spring—>用IDEA开发Spring程序(01)

    一.心动不如行动 一.创建项目 *注:在IDEA中我创建的Maven项目,不了解Maven的朋友可以看我之前的博客“我们一起走进Maven——知己知彼”,了解Maven后可以看我之前的博客“Maven ...

  7. wpf界面按钮自动点击

    Button Button = new Button();Button.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));//在按钮生成时便会自动触 ...

  8. k8s学习02-----kubeadm部署k8s

    机器规划 系统配置 三台机器都执行 1.关闭selinux及firewalld sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux ...

  9. Linux常用命令之权限管理

    在linux中的每一个文件或目录都包含有访问权限,这些访问权限决定了谁能访问和如何访问这些文件和目录,这也让linux更安全.下面主要讲解下常用的权限命令chgrp,chmod,chown . 1.文 ...

  10. 【POJ - 3280】Cheapest Palindrome(区间dp)

    Cheapest Palindrome 直接翻译了 Descriptions 给定一个字符串S,字符串S的长度为M(M≤2000),字符串S所含有的字符的种类的数量为N(N≤26),然后给定这N种字符 ...