webapi框架搭建-安全机制(四)-可配置的基于⾓⾊的权限控制
在上⼀篇的,某个⾓⾊拥有哪些接⼝的权限是⽤硬编码的⽅式写在接⼝上的,如RBAuthorize(Roles = "ur,member"),在⼩的项⽬⾥,其实也够⽤了,但如果项⽬的需求就是要可在后台管理界⾯⾥动态配置某某⾓⾊有某某接⼝的权限怎么办?这编我们⼀起来实现。
⾸先,我们要在数据库⾥存储这些需要权限控制的接⼝,其次,要在上编的RBAuthorizeAttribute的IsAuthorized⽅法⾥重写⾃⼰逻辑。实体设计(数据库表设计)
共4张表:⽤户表、⾓⾊表、资源表、权限表
⽤户表:只记录⽤户的基本信息,如id,⽤户名,姓名,性别,密码等
⾓⾊表:只记录⾓⾊的基本信息,如⾓⾊名,id
资源表:只记录“需要做权限控制的资源”,这些“资源”可以是菜单,接⼝等。
权限表:记录“哪些⾓⾊对哪些资源有访问权限”
⽤户⾓⾊关系表:记录⽤户和⾓⾊的关系教学目的
四个表的实体对应如下
⽤户表
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace webapi.Entities
{
/// <summary>
/// ⽤户表
/// </summary>
[Table("Ur")]
public partial class Ur:BaEntity
{
/// <summary>
ps导出gif/// 主键
/// </summary>
[Key, Column(TypeName = "varchar"), MaxLength(50)]
public string Id { get; t; }
/// <summary>
/// 登录名
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string LoginName { get; t; }
/// <summary>
/// 真实姓名
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string Name { get; t; }
/// <summary>
/// 密码,⽤于存储密码的md5加密
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string Pwd { get; t; }
/
// <summary>
/// 性别,1男2⼥,对应Gender枚举
/// </summary>
[Column(TypeName = "int")]
public int? Gender { get; t; }
/// <summary>
/// ⾝份证
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string IdentityCard { get; t; }
/// <summary>
/
// 电话
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string Tel { get; t; }
/// <summary>
/// 出⽣⽇期
/// </summary>
[Column(TypeName = "datetime")]
public DateTime? Birthdate { get; t; }
/// <summary>
/// 头像
/
// </summary>
[Column(TypeName = "varchar"), MaxLength(500)]
public string UrPic { get; t; }
}
}
⾓⾊表
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace webapi.Entities
{
/// <summary>
/// ⾓⾊表
/
// </summary>
[Table("Role")]
public partial class Role:BaEntity
{
/// <summary>
/// ⾓⾊ID
/// </summary>
[Key, Column(TypeName = "varchar"), MaxLength(50)] public string Id { get; t; }
/// <summary>
/// ⾓⾊名
/// </summary>
[Column(TypeName = "varchar"), MaxLength(20)]
public string Name { get; t; }
}
}
⽤户⾓⾊关系表
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace webapi.Entities
{
/// <summary>
/// ⽤户⾓⾊关系对应表,ur role map
/// </summary>
[Table("URM")]
public partial class URM:BaEntity
{
/// <summary>
/// ID主键
/// </summary>
[Key, Column(TypeName = "varchar"), MaxLength(50)] public string Id { get; t; }
/// <summary>
/// ⽤户ID
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string UrId { get; t; }
/// <summary>
/// ⾓⾊ID
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string RoleId { get; t; }
}
}
资源表
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace webapi.Entities
{
/// <summary>
/// 需要做权限控制的资源
/// </summary>
[Table("Resource")]
public class Resource:BaEntity
{
/// <summary>
/// 主键
/// </summary>
[Key,Column(TypeName = "varchar"),MaxLength(50)]
public string Id { t; get; }
/// <summary>
/// 资源类型,如webapi接⼝,菜单等
/// </summary>
[Column(TypeName = "varchar"), MaxLength(20)]
public string Category { t; get; }
/// <summary>
/// 资源名,如“ControllerName.ActionName”,或是“url地址”
/// </summary>
[Column(TypeName = "varchar"), MaxLength(100)]
public string Name { t; get; }
/
// <summary>
/// 资源描述
/// </summary>
[Column(TypeName = "varchar"), MaxLength(200)]
public string Description { t; get; }
}
}
词的定义
权限表
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace webapi.Entities
{
/// <summary>
/// 权限表,记录⾓⾊和资源的对应关系
/// </summary>
[Table("Permission")]
public class Permission:BaEntity
{
一年级上册试卷/// <summary>
/// 主键
/// </summary>
[Key, Column(TypeName = "varchar"), MaxLength(50)]
public string Id { t; get; }
/// <summary>
/// 资源类型,如webapi接⼝,菜单等
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
public string RoleId { t; get; }
/// <summary>
/// 资源名,如“ControllerName.ActionName”,或是“url地址”
/// </summary>
[Column(TypeName = "varchar"), MaxLength(50)]
墨西哥城市public string ResourceId { t; 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;
identifyingusing webapi.Services;
namespace webapi.Security
{
/// <summary>
/// Role Basic AuthorizeAttribute(基于⾓⾊的授权)
/// </summary>
public class RBAuthorizeAttribute : AuthorizeAttribute
{
public string Description { t; 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.Respon =
actionContext.ControllerContext.Request.CreateErrorRespon(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:BaService
{
public AuthorizeService(DB db) : ba(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;
王雨晴
}
}
}