RBAC(Role-Based Access Control)的核心逻辑是将权限与角色绑定,用户通过关联角色获取对应权限,避免了直接给用户分配权限带来的管理混乱问题,在C#开发的企业级应用中非常常见。

RBAC核心表结构设计
标准的RBAC至少需要五张核心表,分别是用户表、角色表、权限表、用户角色关联表、角色权限关联表,下面是SQL Server的建表示例:
-- 用户表
CREATE TABLE Sys_User (
UserId INT PRIMARY KEY IDENTITY(1,1),
UserName NVARCHAR(50) NOT NULL,
Password NVARCHAR(100) NOT NULL,
CreateTime DATETIME DEFAULT GETDATE()
)
-- 角色表
CREATE TABLE Sys_Role (
RoleId INT PRIMARY KEY IDENTITY(1,1),
RoleName NVARCHAR(50) NOT NULL,
RoleDesc NVARCHAR(200)
)
-- 权限表
CREATE TABLE Sys_Permission (
PermissionId INT PRIMARY KEY IDENTITY(1,1),
PermissionName NVARCHAR(50) NOT NULL,
PermissionCode NVARCHAR(100) NOT NULL, -- 权限唯一标识,如 user:add
PermissionType TINYINT NOT NULL -- 1菜单 2接口 3按钮
)
-- 用户角色关联表
CREATE TABLE Sys_UserRole (
UserId INT,
RoleId INT,
PRIMARY KEY (UserId, RoleId)
)
-- 角色权限关联表
CREATE TABLE Sys_RolePermission (
RoleId INT,
PermissionId INT,
PRIMARY KEY (RoleId, PermissionId)
)
C#实体类定义
对应数据库表结构,我们需要定义对应的C#实体类,用于后续的数据操作:
// 用户实体
public class SysUser
{
public int UserId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public DateTime CreateTime { get; set; }
}
// 角色实体
public class SysRole
{
public int RoleId { get; set; }
public string RoleName { get; set; }
public string RoleDesc { get; set; }
}
// 权限实体
public class SysPermission
{
public int PermissionId { get; set; }
public string PermissionName { get; set; }
public string PermissionCode { get; set; }
public byte PermissionType { get; set; }
}
核心权限服务实现
获取用户所有权限
首先需要实现根据用户ID获取其所有权限的方法,这里使用ADO.NET作为数据访问层示例:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
public class PermissionService
{
private readonly string _connStr = "Server=127.0.0.1;Database=RBAC_DB;Uid=sa;Pwd=123456";
/// <summary>
/// 获取用户所有权限编码
/// </summary>
/// <param name="userId">用户ID</param>
/// <returns>权限编码集合</returns>
public List<string> GetUserPermissions(int userId)
{
List<string> permissionCodes = new List<string>();
string sql = @"
SELECT DISTINCT p.PermissionCode
FROM Sys_User u
JOIN Sys_UserRole ur ON u.UserId = ur.UserId
JOIN Sys_Role r ON ur.RoleId = r.RoleId
JOIN Sys_RolePermission rp ON r.RoleId = rp.RoleId
JOIN Sys_Permission p ON rp.PermissionId = p.PermissionId
WHERE u.UserId = @UserId";
using (SqlConnection conn = new SqlConnection(_connStr))
{
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@UserId", userId);
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
permissionCodes.Add(reader["PermissionCode"].ToString());
}
}
return permissionCodes;
}
}
权限校验实现
实现通用的权限校验方法,支持校验单个权限或者多个权限中的任意一个:
public class PermissionService
{
// 省略之前的代码
/// <summary>
/// 校验用户是否拥有指定权限
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="requiredPermission">需要的权限编码</param>
/// <returns>是否有权限</returns>
public bool HasPermission(int userId, string requiredPermission)
{
List<string> userPermissions = GetUserPermissions(userId);
return userPermissions.Contains(requiredPermission);
}
/// <summary>
/// 校验用户是否拥有任意一个指定权限
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="requiredPermissions">需要的权限编码集合</param>
/// <returns>是否有权限</returns>
public bool HasAnyPermission(int userId, params string[] requiredPermissions)
{
List<string> userPermissions = GetUserPermissions(userId);
foreach (var permission in requiredPermissions)
{
if (userPermissions.Contains(permission))
{
return true;
}
}
return false;
}
}
高级场景实现
接口权限拦截
在ASP.NET Core项目中,可以通过自定义过滤器实现接口级别的权限拦截,避免无权限用户访问接口:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Linq;
public class PermissionFilter : IAuthorizationFilter
{
private readonly string[] _requiredPermissions;
private readonly PermissionService _permissionService;
public PermissionFilter(PermissionService permissionService, params string[] requiredPermissions)
{
_permissionService = permissionService;
_requiredPermissions = requiredPermissions;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
// 假设用户ID存在HttpContext的Item中,实际项目中从登录态获取
int userId = (int)context.HttpContext.Items["UserId"];
if (!_permissionService.HasAnyPermission(userId, _requiredPermissions))
{
context.Result = new JsonResult(new { code = 403, msg = "无权限访问该接口" })
{
StatusCode = 403
};
}
}
}
// 使用方式,在需要权限的Controller或者Action上添加特性
[TypeFilter(typeof(PermissionFilter), Arguments = new object[] { "user:add", "user:edit" })]
[ApiController]
[Route("api/user")]
public class UserController : ControllerBase
{
[HttpPost]
public IActionResult AddUser()
{
return Ok("添加用户成功");
}
}
动态菜单生成
根据用户权限动态生成前端菜单,只展示用户有权限访问的菜单项:
public class MenuService
{
private readonly PermissionService _permissionService;
private readonly string _connStr = "Server=127.0.0.1;Database=RBAC_DB;Uid=sa;Pwd=123456";
public MenuService(PermissionService permissionService)
{
_permissionService = permissionService;
}
/// <summary>
/// 获取用户可访问的菜单列表
/// </summary>
/// <param name="userId">用户ID</param>
/// <returns>菜单集合</returns>
public List<SysPermission> GetUserMenus(int userId)
{
List<string> permissionCodes = _permissionService.GetUserPermissions(userId);
List<SysPermission> menus = new List<SysPermission>();
string sql = "SELECT * FROM Sys_Permission WHERE PermissionType = 1";
using (SqlConnection conn = new SqlConnection(_connStr))
{
SqlCommand cmd = new SqlCommand(sql, conn);
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
SysPermission menu = new SysPermission
{
PermissionId = (int)reader["PermissionId"],
PermissionName = reader["PermissionName"].ToString(),
PermissionCode = reader["PermissionCode"].ToString(),
PermissionType = (byte)reader["PermissionType"]
};
if (permissionCodes.Contains(menu.PermissionCode))
{
menus.Add(menu);
}
}
}
return menus;
}
}
扩展优化建议
实际项目中可以对基础RBAC做以下扩展:
- 增加角色继承功能,子角色可以继承父角色的所有权限,减少重复配置
- 支持权限的黑白名单配置,针对特殊用户做权限例外处理
- 增加权限变更日志,记录所有角色、权限的修改操作,方便审计
- 将权限数据缓存到Redis中,减少数据库查询次数,提升性能
以上就是在C#中实现RBAC权限控制的完整方案,开发者可以根据自身项目的需求调整表结构和实现逻辑,搭建符合业务要求的权限体系。