ASP.NET Core 2.2 : 二十七. JWT与用户授权(细化到Action)

上一章分享了如何在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("001"))
            {
                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,"001");
}));

在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 = "001",
            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 = "002",
            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

原文地址:https://www.cnblogs.com/FlyLolo/p/ASPNETCore2_27.html

时间: 2024-08-27 20:52:21

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

ASP.NET CORE系列【二】使用Entity Framework Core进行增删改查

原文:ASP.NET CORE系列[二]使用Entity Framework Core进行增删改查 介绍 EntityFrameworkCore EF core 是一个轻量级的,可扩展的EF的跨平台版本.对于EF而言 EF core 包含许多提升和新特性,同时 EF core 是一个全新的代码库,并不如 EF6 那么成熟和稳定.EF core 保持了和EF相似的开发体验,大多数顶级API都被保留了下来,所以,如果你用过EF6,那么上手EF core你会觉得非常轻松和熟悉,EF core 构建在一

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

来源:https://www.cnblogs.com/FlyLolo/p/ASPNETCore2_26.html 本文将通过实际的例子来演示如何在ASP.NET Core中应用JWT进行用户认证以及Token的刷新方案(ASP.NET Core 系列目录) 一.什么是JWT? JWT(json web token)基于开放标准(RFC 7519),是一种无状态的分布式的身份验证方式,主要用于在网络应用环境间安全地传递声明.它是基于JSON的,所以它也像json一样可以在.Net.JAVA.Jav

Asp.Net Core在线生成二维码

前言: 原先用zxing Code写过基于Winfrom的批量生成二维码工具,以及单个生成二维码工具:批量生成二维码Gihub源代码 今天尝试用QRCoder 加 Asp.Net Core 写了一个在线生成二维码的例子,并且保存图片到Ubuntu系统: 代码: 生成二维码所需要用到的包:QRCoder 根据Github上的源代码,引用NuGet包 PM> Install-Package QRCoder 根据传入的参数,生成二维码,并且保存图片 public static void QRCode(

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

上一章讲了系统如何将客户端提交的请求数据格式化处理成我们想要的格式并绑定到对应的参数,本章讲一下它的“逆过程”,如何将请求结果按照客户端想要的格式返回去.(ASP.NET Core 系列目录) 一.常见的返回类型 以系统模板默认生成的Home/Index这个Action来说,为什么当请求它的时候回返回一个Html页面呢?除了这之外,还有JSON.文本等类型,系统是如何处理这些不同的类型的呢? 首先来说几种常见的返回类型的例子,并用Fiddler请求这几个例子看一下结果,涉及到的一个名为Book的

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

上一章介绍了配置的多种数据源被注册.加载和获取的过程,本节看一下这个过程系统是如何实现的.(ASP.NET Core 系列目录) 一.数据源的注册 在上一节介绍的数据源设置中,appsettings.json.命令行.环境变量三种方式是被系统自动加载的,这是因为系统在webHost.CreateDefaultBuilder(args)中已经为这三种数据源进了注册,那么就从这个方法说起.这个方法中同样调用了ConfigureAppConfiguration方法,代码如下: public stati

从零开始构建一个的asp.net Core 项目(二)

接着上一篇博客继续进行.上一篇博客只是显示了简单的MVC视图页,这篇博客接着进行,连接上数据库,进行简单的CRUD. 首先我在Controllers文件夹点击右键,添加->控制器 弹出的对话框中选择miniual Dependencies.在项目的根目录下添加一个Models文件夹,在该文件夹下添加一个Users.cs类.(该类在数据库中对应一张表,表名为Users 里边有三个字段 其中ID是主键,自增的.) public class Users { [Key] public int Id {

ASP.NET Core SignalR (二):支持的平台

此为系列文章,对MSDN ASP.NET Core SignalR 的官方文档进行系统学习与翻译.其中或许会添加本人对 ASP.NET Core 的浅显理解. 服务端系统要求 只要是ASP.NET Core支持的服务器平台都会支持ASP.NET Core SignalR. Javascript 客户端 JavaScript客户端 运行在NodeJS 8 以及后续版本中,支持的浏览器如下: 浏览器 版本 Microsoft Edge Current† Mozilla Firefox Current

ASP.NET CORE系列【四】基于Claim登录授权

介绍 关于什么是Claim? 可以看看其他大神的文章: http://www.cnblogs.com/jesse2013/p/aspnet-identity-claims-based-authentication-and-owin.html http://www.cnblogs.com/savorboard/p/aspnetcore-identity.html 注:本人目前还是菜鸟初学阶段,如有写错的地方,望各位大鸟 指出! 场景 用户登录是一个非常常见的应用场景 .net core的登录方式跟

ASP.NET Core 折腾笔记二:自己写个完整的Cache缓存类来支持.NET Core

背景: 1:.NET Core 已经没System.Web,也木有了HttpRuntime.Cache,因此,该空间下Cache也木有了. 2:.NET Core 有新的Memory Cache提供,不过该内存类我看了一下,并没有支持文件的缓存依赖. 因此,在此前提下,预计.NET Core明年出来2.0版本时,可能也没支持文件的缓存依赖,因此,有必要提前准备实现. 在写此文前,我扫了一下园子里关于自定义缓存类的相关文章. 发现很多自定义的缓存类文章都简单停留在对字典的增删改查. 因此,决定补充