跟我一起学.NetCore之熟悉的接口权限验证不能少(Jwt)

前言

权限管控对于一个系统来说是非常重要的,最熟悉不过的是菜单权限和数据权限,上一节通过Jwt实现了认证,接下来用它实现接口权限的验证,为什么不是菜单权限呢?对于前后端分离而言,称其为接口权限感觉比较符合场景(我是这么理解的);数据权限牵涉到具体业务,这里就不说啦!

正文

对于一些比较简单的系统,访问角色可能只有固定的几种,比如一些产品管理系统,通常只有管理员、维护员、用户三种权限,管理员拥有整个系统的权限,维护员只能访问产品维护相关页面和操作,用户只能访问产品的一些信息,如果类似这种情况,可以直接指定角色的方式进行权限管控,如下:

案例代码直接使用上一节的项目,借用上次认证那块代码(偷懒太明显~~~),如果没看上一篇的小伙伴,去瞅瞅认证那块内容(跟我一起学.NetCore之WebApi接口裸奔有风险(Jwt)),随便敲敲代码,这节要用(别说我,我是有苦衷的,想让小伙伴多撸代码~~~);如果只是想熟悉知识点,也可以继续往下看的,不废话,直接开始:

注:[Authorize]加在控制器上时,该控制器下所有的接口都受保护;

为了方便测试,如上图所示,增加一个产品控制器,针对不同人员模拟了三个接口,因为接口受到保护,只能通过获取到的Token才能正常访问,这里就不截图演示了。Token调用User中的Login接口获取,详细请参考(跟我一起学.NetCore之WebApi接口裸奔有风险(Jwt))。哎呀,还是上个生成Token的代码图:

下面将三个接口指定为不同角色访问,然后运行访问如下:

通过运行测试可知,当增加了对应角色要求之后,尽快Token验证正确,也不能正常调用接口,返回403禁止访问。已经为接口指定了角色,那要如何才能正常调用呢?其实只要在生成Token的时候指定对应角色即可正常访问对应角色的接口,如下代码优化:

如上运行所示,在生成Token时指定了角色为Admin,则这个Token只能访问指定角色为Admin的接口,其他接口是不能访问的。如需要访问其他接口,同样需要在生成Token的时候添加对应的角色,如下:

运行效果这里就不截图演示了,小伙伴们自己试试。

看到这,小伙伴们肯定会问,管理员角色肯定是所有接口都能访问,拿到了Token接下来该咋办,每个接口都加管理员角色对应的特性吗?对于多个角色访问同一个接口的情况,一般会为授权定义策略来实现,如下:

运行效果如下:

小注意点:

  • 多个角色或运算:多个角色只要有其中一个就可以访问;

  • 多个角色且运算:同时得有多个角色才能访问接口;

到这,相信小伙伴已经忍不住要问:不管是角色还是策略的方式,角色都写死了,如果角色动态分配权限咋搞? 是的,上面的方式只适合对权限管控比较简单的项目,绝大数的项目权限肯定是动态分配的,即根据需求,可以针对用户进行访问权限配置,所以接下来就说说这块咋搞。

这次增加的代码稍微有点多,代码都有注释,另外跟着我标注的步骤走,绝对No Problem:

通过以上步骤就完成动态权限的验证了,是不是很给力,这里需要注意一下几个点:

  • 后续用到IHttpContextAccessor需要进行注册;


  • 用户ID在登录的时候要放入Payload中,后续权限验证时要用;

  • API中使用的策略名称要和定义的一致;

以上案例演示中,在登录的时候模拟权限数据存入内存,对于一些用户数据不大的项目,这种方式还不错,但是对于用户数量比较大或者分布式部署的项目,建议将权限数据存入Redis等缓存数据库中,存取统一的同时也能减少对数据库的访问压力,总不能每次请求过来都从数据库中获取权限数据进行校验。

小知识点:

  • 通过获取用户时,访问的接口必须要有Authorize特性,否则只能通过自己解析Token获取;

  • 如果在统一受保护的控制器中,有个别接口不需要权限验证,可以为其标注AllowAnonymous特性即可;

校验权限逻辑的完整代码如下:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;namespace SwaggerDemo.Permission
{/// <summary>/// 权限处理的关键类/// </summary>public class PermissionHandler : AuthorizationHandler<PermissionRequirement>{/// <summary>/// 通过IHttpContextAccessor可以获取HttpContext相关信息,但一定要注册服务/// </summary>private readonly IHttpContextAccessor _accessor;/// <summary>/// 用于判断请求是否带有凭据和是否登录/// </summary>public IAuthenticationSchemeProvider Scheme { get; set; }/// <summary>///  构造函数注入/// </summary>public PermissionHandler(IHttpContextAccessor accessor,IAuthenticationSchemeProvider scheme){this._accessor = accessor;Scheme = scheme;}protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement){try{//拿到HttpContext就无所不能啦var httpContext = _accessor.HttpContext;//判断资源数据中权限列表中是否有权限if (!requirement.Permissions.Any()){//没有直接返回无权限,也可以重新获取权限,实现不退出重新登录就可获取最新权限context.Fail();}//判读请求是否拥有凭据,即是否登录var defaultAuthenticate = await Scheme.GetDefaultAuthenticateSchemeAsync();if (defaultAuthenticate == null){context.Fail();}var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);//不为空代表登录成功,为空登录失败if (result?.Principal != null){// 获取生成Token时放在payload里的userIdstring userId = _accessor.HttpContext.User.FindFirst("userId").Value;// 获取当前请求的地址string requestUrl = httpContext.Request.Path.Value.ToLower();// 从权限表中查找当前用户是否有当前请求地址的权限var permission = requirement.Permissions.FirstOrDefault(a => a.Url.ToLower() == requestUrl && a.UserId == userId);// 如果没找到,代表没有权限if (permission == null){context.Fail();}// 如果找到,就继续往下执行context.Succeed(requirement);}else{// 获取不到对应值就返回无权限context.Fail();}}catch (Exception ex){context.Fail();}}}
}

总结

关于权限验证这块之前大多都是在MVC的授权过滤器中进行完成的, 本来想在说过滤器那块一起说说权限验证的,但感觉关于Jwt的放在一块比较合适。关于Jwt其实还有一个比较关键的点,就是对于Token的处理问题,比如刷新Token、手动失效Token等,这块后续单独整理一篇内容分享。下次先说说过滤器的执行顺序。

关于代码,其实现在在写案例的时候感觉已经开始复杂了,后面整理整理会放在github上,如果急需的可以私下找我,我单独发给小伙伴。

一个被程序搞丑的帅小伙,关注"Code综艺圈",识别关注跟我一起学~~~

撸文不易,莫要白瞟,三连走起~~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307389.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.NET 云原生架构师训练营(模块一 架构师与云原生)--学习笔记

目录什么是软件架构软件架构的基本思路单体向分布式演进、云原生、技术中台1.1 什么是软件架构1.1.1 什么是架构&#xff1f;Software architecture {Elements, Forms, Rationale/Constraints}元素、形式/模式、基本原理和限制为什么需要软件架构&#xff1f;软件架构的终极目…

leetcode530. 二叉搜索树的最小绝对差

一:题目 二:上码 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

再分享 5 个 vs 调试技巧

前言 之前在《5 个非常实用的 vs 调试技巧》和《继续分享 5 个实用的 vs 调试技巧》中分享了 10 个我认为非常值得了解的 vs 调试技巧&#xff0c;本周继续分享 5 个很实用的调试技巧。1. 显示下一条语句 在调试时&#xff0c;遇到断点中断后&#xff0c;为了更加清楚的了解程…

leetcode501. 二叉搜索树中的众数

一:题目 二:上码 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

网络知识 | 《图解HTTP》读书笔记(上)

【网络知识】| 作者 / Edison Zhou这是EdisonTalk的第293篇原创内容作为一个专业的IT技术人&#xff0c;一个Web应用开发者&#xff0c;不了解网络基础和协议&#xff0c;怎么能行&#xff1f;本文是我2016年阅读《图解HTTP》一书的读书笔记&#xff0c;希望对你有所帮助&#…

leetcode292周赛--6057. 统计值等于子树平均值的节点数

一:题目 二:上码 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

C# 中 System.Range 结构体

翻译自 John Demetriou 2020年4月6日 的文章 《C# 8 Is Introducing Ranges》我们之前讨论过的 C# 中的一个特性 System.Index 结构体&#xff0c;与另一特性 System.Range 结构体 是携手并进的。在 C# 8.0 之前&#xff0c;没有访问集合中任意部分的范围或切片的语法。开发人员…

leetcode292周赛--6056. 字符串中最大的 3 位相同数字

一:题目 二:上码 class Solution { public:string largestGoodInteger(string num) {int slowIndex 0;int count 0;vector<int> ans;for (int fastIndex 1; fastIndex < num.size(); fastIndex) {if (num[fastIndex] ! num[slowIndex]) {slowIndex;} else {//相等…

GDB 调试 .NET 程序实录 - .NET 调用 .so 出现问题怎么解决

注&#xff1a;本文重要信息使用 *** 屏蔽关键字。最近国庆前&#xff0c;项目碰到一个很麻烦的问题&#xff0c;这个问题让我们加班到凌晨三点。大概背景&#xff1a;客户给了一些 C语言 写的 SDK 库&#xff0c;这些库打包成 .so 文件&#xff0c;然后我们使用 C# 调用这些库…

leetcode236. 二叉树的最近公共祖先(java递归)

一:题目 二:上码 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(int x) { val x; }* }*/ class Solution {/**思路:1.后序遍历 从下往上遍历 找到目标结点后然后做逻辑判断处理1…

WPF MVVM 弹框之等待框

WPF MVVM 弹框之等待框目录一、效果二、弹框主体改造三、等待动画用户控件四、弹窗 ViewModel 和帮助类的改造五、使用方法和代码地址独立观察员 2020年10月13日之前写过一篇《WPF MVVM 模式下的弹窗》&#xff0c;里面实现了确认框和消息框&#xff0c;经过一段时间的演化&…

设计一个具有等待队列的连接池

说到连接池相关很多人都使用过&#xff0c;常见的有数据连接池&#xff0c;HttpClient连接池等。连接池的作用是保持一定量的连接让交互过程复用这些连接&#xff0c;从而大大节省连接创建过程或过多的损耗。在连接池策略中往往当池没有连接的情况都会抛出异常告诉使用者资源无…

leetcode701. 二叉搜索树中的插入操作

一:题目 二:上码 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

你不该错过的2020中国开源年报,填开源开发者问卷,成为国内开源的见证者

点击上方“开源社”关注我们| 作者&#xff1a;王伟| 编辑&#xff1a;黄欣宜| 设计&#xff1a;冯艺怡| 责编&#xff1a;王玥敏卷首语一年一度的中国开源年报再度启动&#xff5e;中国开源年报由开源社发起。旨在从多种维度&#xff0c;多种方式&#xff0c;多种协作来呈现国…

leetcode450. 删除二叉搜索树中的节点(java详解版)

一:题目 二:上码 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

.NET Core使用FluentEmail发送邮件

前言在实际的项目开发中&#xff0c;我们会遇到许多需要通过程序发送邮件的场景&#xff0c;比如异常报警、消息、进度通知等等。一般情况下我们使用原生的SmtpClient类库居多&#xff0c;它能满足我们绝大多数场景。但是使用起来不够简洁&#xff0c;许多场景需要我们自行封装…

进击吧! Blazor !第四期 组件开发

Blazor 是一个 Web UI 框架&#xff0c;可通过 WebAssembly 在任意浏览器中运行 .Net 。Blazor 旨在简化快速的单页面 .Net 浏览器应用的构建过程&#xff0c;它虽然使用了诸如 CSS 和 HTML 之类的 Web 技术&#xff0c;但它使用 C&#xff03;语言和 Razor 语法代替 JavaScrip…

HttpReports 2.0 发布了 !!!

https://www.cnblogs.com/myshowtime/p/13806631.html来源???? 前言介绍HttpReports 是基于.Net Core 开发的APM监控系统&#xff0c;使用MIT开源协议&#xff0c;主要功能包括&#xff0c;统计, 分析, 可视化&#xff0c; 监控&#xff0c;追踪等&#xff0c;适合在微服务…

.NET Standard 来日苦短去日长

作者&#xff1a;Richard翻译&#xff1a;精致码农-王亮原文&#xff1a;http://dwz.win/Q4h自从 .NET 5 宣贯以来&#xff0c;很多人都在问这对 .NET Standard 意味着什么&#xff0c;它是否仍然重要。在这篇文章中&#xff0c;我将解释 .NET 5 是如何改进代码共用并取代 .NET…

leetcode538. 把二叉搜索树转换为累加树

一:题目 二:上码 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …