如何在MVC 5中为特定用户实现基于数据库的自定义授权?
基于Owin/Identity实现细粒度CRUD权限校验方案
我明白作为MVC新手,对着权限校验这块确实容易懵——虽然网上讨论不少,但要结合自己的数据库表和Owin/Identity落地还是挺头疼的。针对你说的基于User_MenuRole和User_MenuDetails表做细粒度CRUD权限控制,我给你梳理一套可落地的方案:
一、先明确表结构关联逻辑
首先得把你的两张表关系理清楚(假设你的表结构是这样,若有差异可调整):
- User_MenuDetails:存储系统的功能菜单/操作点,比如包含
MenuId(主键)、MenuName(如"采购订单")、ActionName(可选,对应MVC的Action)等字段,每条记录对应一个业务模块的操作集合。 - User_MenuRole:存储用户与权限的关联,包含
UserId(关联Identity用户Id)、MenuId(关联User_MenuDetails的主键)、CanCreate、CanEdit、CanDelete、CanRead(布尔型,标记是否拥有对应权限)。
二、登录时加载用户权限到Identity
Owin/Identity的用户身份信息存在ClaimsIdentity里,我们可以在用户登录成功后,把他的权限作为Claim添加进去,这样后续校验时就能直接从身份信息里读取:
在AccountController的Login方法中修改登录逻辑:
var user = await UserManager.FindAsync(model.Email, model.Password); if (user != null) { // 查询该用户的所有权限记录,关联菜单详情 var userPermissions = db.User_MenuRole .Include(r => r.User_MenuDetails) .Where(r => r.UserId == user.Id) .ToList(); // 创建Identity身份对象 var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); // 逐个添加权限Claim foreach (var permission in userPermissions) { var menu = permission.User_MenuDetails; // 按"菜单名_权限类型"的格式定义Claim值,方便后续识别 if (permission.CanCreate) identity.AddClaim(new Claim("Permission", $"{menu.MenuName}_Create")); if (permission.CanEdit) identity.AddClaim(new Claim("Permission", $"{menu.MenuName}_Edit")); if (permission.CanDelete) identity.AddClaim(new Claim("Permission", $"{menu.MenuName}_Delete")); if (permission.CanRead) identity.AddClaim(new Claim("Permission", $"{menu.MenuName}_Read")); } // 完成登录 AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = model.RememberMe }, identity); return RedirectToAction("Index", "Home"); }
三、自定义权限校验特性
为了方便在Controller的Action上标记权限要求,我们可以自定义一个AuthorizePermission特性,继承自Identity的AuthorizeAttribute:
public class AuthorizePermissionAttribute : AuthorizeAttribute { // 允许传入需要的权限,比如"采购订单_Create" public string RequiredPermission { get; set; } protected override bool AuthorizeCore(HttpContextBase httpContext) { // 先校验用户是否已登录 if (!base.AuthorizeCore(httpContext)) return false; // 检查当前用户是否包含指定的权限Claim var currentUser = httpContext.User; return currentUser.HasClaim("Permission", RequiredPermission); } // 没有权限时的处理:返回403或跳转到无权限页面 protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden); // 如果需要跳转页面,可以替换成: // filterContext.Result = new RedirectResult("/Home/AccessDenied"); } }
然后在需要权限校验的Action上直接使用:
// 只有拥有"采购订单_Create"权限的用户才能访问 [AuthorizePermission(RequiredPermission = "采购订单_Create")] public ActionResult Create() { return View(); } // 只有拥有"采购订单_Delete"权限的用户才能访问 [AuthorizePermission(RequiredPermission = "采购订单_Delete")] public ActionResult Delete(int id) { // 删除逻辑 return RedirectToAction("Index"); }
四、视图层控制元素显示
除了Action层面的校验,我们还需要在视图里隐藏用户没有权限的按钮/链接,提升用户体验:
<!-- 只有拥有创建权限才显示按钮 --> @if (User.HasClaim("Permission", "采购订单_Create")) { <a href="@Url.Action("Create")" class="btn btn-primary">新建采购订单</a> } <!-- 只有拥有删除权限才显示删除按钮 --> @foreach (var order in Model.PurchaseOrders) { <tr> <td>@order.OrderNumber</td> <td> <a href="@Url.Action("Edit", new { id = order.Id })" class="btn btn-sm btn-warning">编辑</a> @if (User.HasClaim("Permission", "采购订单_Delete")) { <a href="@Url.Action("Delete", new { id = order.Id })" class="btn btn-sm btn-danger">删除</a> } </td> </tr> }
五、补充说明
如果你的需求是更细粒度的权限(比如用户只能编辑自己创建的采购订单),那还需要在User_MenuRole或业务表(如PurchaseOrder)里添加关联字段,在Action校验时额外判断订单的创建者是否为当前用户,不过这属于业务逻辑级别的校验,和菜单级CRUD权限可以结合使用。
内容的提问来源于stack exchange,提问作者Parvez
相关产品推荐
相关产品推荐

