webapi框架搭建-安全机制(四)-可配置的基于角色的权限控制

时间:2023-01-03 03:26:55

webapi框架搭建系列博客

  在上一篇的webapi框架搭建-安全机制(三)-简单的基于角色的权限控制,某个角色拥有哪些接口的权限是用硬编码的方式写在接口上的,如RBAuthorize(Roles = "user,member"),在小的项目里,其实也够用了,但如果项目的需求就是要可在后台管理界面里动态配置某某角色有某某接口的权限怎么办?这编我们一起来实现。

  首先,我们要在数据库里存储这些需要权限控制的接口,其次,要在上编的RBAuthorizeAttribute的IsAuthorized方法里重写自己逻辑。

实体设计(数据库表设计)

  共4张表:用户表、角色表、资源表、权限表

  用户表:只记录用户的基本信息,如id,用户名,姓名,性别,密码等

  角色表:只记录角色的基本信息,如角色名,id

  资源表:只记录“需要做权限控制的资源”,这些“资源”可以是菜单,接口等。

  权限表:记录“哪些角色对哪些资源有访问权限”

  用户角色关系表:记录用户和角色的关系

四个表的实体对应如下

用户表

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace webapi.Entities
{
/// <summary>
/// 用户表
/// </summary>
[Table("User")]
public partial class User:BaseEntity
{
/// <summary>
/// 主键
/// </summary>
[Key, Column(TypeName = "varchar"), MaxLength(50)]
public string Id { get; set; }
/// <summary>
/// 登录名
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string LoginName { get; set; }
/// <summary>
/// 真实姓名
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string Name { get; set; }
/// <summary>
/// 密码,用于存储密码的md5加密
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string Pwd { get; set; }
/// <summary>
/// 性别,1男2女,对应Gender枚举
/// </summary>
[Column(TypeName = "int")]
public int? Gender { get; set; }
/// <summary>
/// 身份证
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string IdentityCard { get; set; }
/// <summary>
/// 电话
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string Tel { get; set; }
/// <summary>
/// 出生日期
/// </summary>
[Column(TypeName = "datetime")]
public DateTime? Birthdate { get; set; }
/// <summary>
/// 头像
/// </summary>
[Column(TypeName = "varchar"), MaxLength(500)]
public string UserPic { get; set; }
}
}

  角色表

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace webapi.Entities
{
/// <summary>
/// 角色表
/// </summary>
[Table("Role")]
public partial class Role:BaseEntity
{
/// <summary>
/// 角色ID
/// </summary>
[Key, Column(TypeName = "varchar"), MaxLength(50)]
public string Id { get; set; }
/// <summary>
/// 角色名
/// </summary>
[Column(TypeName = "varchar"), MaxLength(20)]
public string Name { get; set; } }
}

  用户角色关系表

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace webapi.Entities
{
/// <summary>
/// 用户角色关系对应表,user role map
/// </summary>
[Table("URM")]
public partial class URM:BaseEntity
{
/// <summary>
/// ID主键
/// </summary>
[Key, Column(TypeName = "varchar"), MaxLength(50)]
public string Id { get; set; }
/// <summary>
/// 用户ID
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string UserId { get; set; }
/// <summary>
/// 角色ID
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string RoleId { get; set; } }
}

  资源表

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace webapi.Entities
{
/// <summary>
/// 需要做权限控制的资源
/// </summary>
[Table("Resource")]
public class Resource:BaseEntity
{
/// <summary>
/// 主键
/// </summary>
[Key,Column(TypeName = "varchar"),MaxLength(50)]
public string Id { set; get; }
/// <summary>
/// 资源类型,如webapi接口,菜单等
/// </summary>
[Column(TypeName = "varchar"), MaxLength(20)]
public string Category { set; get; }
/// <summary>
/// 资源名,如“ControllerName.ActionName”,或是“url地址”
/// </summary>
[Column(TypeName = "varchar"), MaxLength(100)]
public string Name { set; get; }
/// <summary>
/// 资源描述
/// </summary>
[Column(TypeName = "varchar"), MaxLength(200)]
public string Description { set; get; } }
}

  权限表

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace webapi.Entities
{
/// <summary>
/// 权限表,记录角色和资源的对应关系
/// </summary>
[Table("Permission")]
public class Permission:BaseEntity
{
/// <summary>
/// 主键
/// </summary>
[Key, Column(TypeName = "varchar"), MaxLength(50)]
public string Id { set; get; }
/// <summary>
/// 资源类型,如webapi接口,菜单等
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string RoleId { set; get; }
/// <summary>
/// 资源名,如“ControllerName.ActionName”,或是“url地址”
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string ResourceId { set; get; }
}
}

  

RBAuthorizeAttribute代码修改

核心代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Principal;
using System.Web.Http;
using System.Web.Http.Controllers;
using webapi.Entities;
using webapi.Services; namespace webapi.Security
{
/// <summary>
/// Role Basic AuthorizeAttribute(基于角色的授权)
/// </summary>
public class RBAuthorizeAttribute : AuthorizeAttribute
{
public string Description { set; get; }
protected override bool IsAuthorized(HttpActionContext actionContext)
{ // 下在可替换成自己的授权逻辑代码
AuthorizeService authorizeService =new AuthorizeService(new DB());
var resourceName = actionContext.ActionDescriptor.GetCustomAttributes<RBAuthorizeAttribute>().Any()
? actionContext.ActionDescriptor.ActionName
: actionContext.ControllerContext.ControllerDescriptor.ControllerName;
var roleNames = authorizeService.GetResourceRoleNames(resourceName);
IPrincipal principal = actionContext.ControllerContext.RequestContext.Principal;
return principal != null && principal.Identity != null
&& principal.Identity.IsAuthenticated &&
(
(((IEnumerable<string>)roleNames).Any<string>(new Func<string, bool>(principal.IsInRole)))
);
} protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
actionContext.Response =
actionContext.ControllerContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "未授权");
}
}
}

  说明:在IsAuthorized方法里判断用户是否有某个资源的权限。下面是我的思路

1)获取用户的角色

  在前面的博客里,IdentityBasicAuthentication已经从http请求头里将token解密并知道了用户的角色,并将角色写入到了IPrincipal对象,所以RBAuthorizeAttribute只要从IPrincipal里取出来就行,即如下的代码:IPrincipal principal = actionContext.ControllerContext.RequestContext.Principal;

  2)获取该请求资源的所有角色数组

  我创建了authorizeService类,用于处理这一个逻辑,博友们可以下载我的框架,只要改一下authorizeService类里的GetResourceRoleNames方法就行。 

  3)判断是否有权限

    首先principal不能为空,且principal.Identity是已经通过身份验证的(即Identity.IsAuthenticated==true),并且用户的角色在资源权限角色数组里。

 return principal != null && principal.Identity != null
&& principal.Identity.IsAuthenticated &&
(
(((IEnumerable<string>)roleNames).Any<string>(new Func<string, bool>(principal.IsInRole)))
);

各辅助代码也附上

AuthorizeService代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using webapi.Entities; namespace webapi.Services
{
public class AuthorizeService:BaseService
{
public AuthorizeService(DB db) : base(db)
{
}
/// <summary>
/// 获取资源的角色名数组
/// </summary>
/// <param name="resourceName"></param>
/// <returns></returns>
public string[] GetResourceRoleNames(string resourceName)
{
var resource=_db.Resources.FirstOrDefault(a => a.Name == resourceName);
var roleIds = _db.Permissions.Where(a => a.ResourceId == resource.Id).Select(a => a.RoleId).ToArray();
var roleNames = _db.Roles.Where(a => roleIds.Contains(a.Id)).Select(a => a.Name).ToArray();
return roleNames;
}
}
}

  

源码地址:https://github.com/shengyu-kmust/webapi.git

webapi框架搭建-安全机制(四)-可配置的基于角色的权限控制的更多相关文章

  1. webapi框架搭建-安全机制(三)-简单的基于角色的权限控制

    webapi框架搭建系列博客 上一篇已经完成了“身份验证”,如果只是想简单的实现基于角色的权限管理,我们基本上不用写代码,微软已经提供了authorize特性,直接用就行. Authorize特性的使 ...

  2. webapi框架搭建-安全机制(一)

    本系列博客链接:webapi框架搭建系列博客 前言 webapi接口是开放给外部使用的,包括接口的地址,传参的规范,还有返回结果的说明.正因为接口的开放性,使得接口的安全很重要.试想一下,用抓包工具( ...

  3. webapi框架搭建-安全机制(二)-身份验证

    webapi框架搭建系列博客 身份验证(authentication)的责任是识别出http请求者的身份,除此之外尽量不要管其它的事.webapi的authentication我用authentica ...

  4. webapi框架搭建系列博客

    webapi框架搭建系列博客 webapi框架搭建-创建项目(一) webapi框架搭建-创建项目(二)-以iis为部署环境的配置 webapi框架搭建-创建项目(三)-webapi owin web ...

  5. webapi框架搭建-创建项目(二)-以iis为部署环境的配置

    上篇:webapi快速框架搭建-创建项目(一) 在"创建项目(一)"这一篇里已经创建了一个空的项目,但项目上什么都没有,本篇描述如何将webapi配置成部署在iis上. 步骤 用n ...

  6. webapi框架搭建-创建项目(三)-webapi owin

    上一篇:创建项目(二) 在上一篇里,我们已经创建好了webapi应用,并已经部署到iis里,本篇讲如何用owin自宿主或是iis宿主来部署webapi应用. owin介绍 传统的asp.net网站只能 ...

  7. webapi框架搭建-日志管理log4net

    前言 本篇讲怎么在前几篇已经创建好的项目里加上日志处理机制,我们采用Log4net技术.跟多的log4net技术的细节请查阅log4net的官网. log4net官网:http://logging.a ...

  8. webapi框架搭建-webapi异常处理

    webapi框架搭建系列博客 前言 上一篇我们已经完成了项目的日志管理,在项目开发中日志会经常记录程序中的异常,供后续问题排查使用.本篇讲如何在webapi里加入异常处理机制. 目的和原则 1.程序任 ...

  9. webapi框架搭建-数据访问ef code first

    webapi框架搭建系列博客 为什么用ef? 我相信很多博友和我一样都有这种“选择困难症”,我曾经有,现在也有,这是技术人的一个通病——总想用“更完美”的方式去实现,导致在技术选择上犹豫不决,或总是推 ...

随机推荐

  1. 练习JavaScript实现梯形乘法表

    效果: 表格用html中的table,tr,td,然后利用for语句实现,循环输出行和列,再根据行列的数量进行乘法运算,第一个for循环输出9行,然后内嵌一个for,在条件表达式中取第一个for循环的 ...

  2. 系统修改利器XueTr

    Windows系统修改利器XueTr 周银辉 在Windows下如果遇到某些进程弄死结束不了,某些文件弄死删不掉,拷贝不出来 (可能是因为你没有管理员权限,可能是因为人家是病毒,可能是系统保护文件,可 ...

  3. Nam Game

    哪一方最终给对方留下4的倍数个石头则win,即想方设法的给对方留下4的倍数个石头. 例如: 9(B win) A:3 B:2(B取2,给对方余4,对方则lose) A:1 | 2 | 3 B:3 | ...

  4. avalon2的后端渲染实践

    avalon2为了提高性能,采用全新的架构,四层架构,其中一层为虚拟DOM. 虚拟DOM的一个好处是能大大提高性能,另一个好处是能过错整描述我们的页面结构.因此在非浏览器环境下,虚拟DOM也能正常运行 ...

  5. Ubuntu命令--CURL用法

    curl命令是个功能强大的网络工具,支持通过http.ftp等方式下载文件.上传文件.还可以用来抓取网页.网络监控等方面的开发,解决开发过程中遇到的问题. 常用参数curl命令参数很多,这里只列出我曾 ...

  6. POJ 1845 Sumdiv (求某个数的所有正因子的和)

    题意: 求A^B的所有正因子的和,最后模9901的结果. 思路: 若对一个数n进行素数分解,n=p1^a1*p2^a2*p3^a3*...*pk^ak那么n的所有正因子之和sum=(1+p1+...+ ...

  7. ehcache简单使用

    项目中需要实现一个功能,定时查询FTP服务器某个目录下的文件,并及时下载至本机,同时不能消耗太多系统资源. 最后实现是使用ehcache,将文件路径和文件大小缓存,如果前后两次无变化,则忽略.如果同一 ...

  8. linux 命令及进程控制

    main.c  main.o/main.obj  main/main.exe          编译                连接 程序运行;      两步: gcc/g++  -c  mai ...

  9. TortoiseSVN上次文件显示被锁定

    1.可以使用SVN clean up来清除锁定. 2.如果不是本目录锁定,系统提示上一层目录锁定,需要到上一层或者根目录中清除. 3.如果在根目录下都无法clean的话,一般采取的方法是另外找一个目录 ...

  10. 转:创建WebTest插件

    •Web测试插件为隔离Web测试中各个主声明语句外部的代码提供了一种手段.自定义的Web测试插件为在运行Web测试时调用某些代码提供了途径.在每个测试迭代中,Web测试插件都要运行一次. •通过从We ...