2018教程之mvc+ef + identity之七:角色权限系统菜单1
不同角色的人登录系统后,能看到不同的菜单这个这个太实用了。我还在同步思考,是不是前台也有这个问题呀,前台菜单,不同的人来可以看见不同的前台菜单哩,暂时这里只讨论后台权限菜单。
众所周知,权限系统是每个系统里面必备的最基本的系统,然而权限系统设计有时挺麻烦的,,现在整理了下,给正在开发此模块的朋友一个思路!
设计基础:用户、角色、权限三大核心表,加上用户角色、角色权限两个映射表(用于给用户表联系上权限表)。这样就可以通过登录的用户来获取权限列表,或判断是否拥有某个权限。
大致用到5张表:用户表(AspNetUsers)、角色表(AspNetRoles)、菜单表(MenuInfo)、用户角色表(AspNetUserRoles)、角色菜单表(RoleMenu)。
各表的大体表结构如下:
1、用户表(UserInfo):Id、UserName、UserPwd,......
2、角色表(RoleInfo):Id、RoleName、......
3、菜单表(MenuInfo):Id、MenuName、......
4、用户角色表(UserRole):Id、UserId、RoleId
5、角色菜单表(RoleMenu):Id、RoleId、MenuId
最关键的地方是,某个用户登录时,如何查找该用户的菜单权限?其实一条语句即可搞定:
假如用户的用户名为Arthur,则他的菜单权限查询如下:
Select m.Id,m.MenuName from MenuInfo m ,UserInfo u, UserRole ur, RoleMenu rm Where m.Id = rm.MenuId and ur.RoleId = rm.RoleId and ur.UserId = u.Id and u.UserName = 'Arthur'
上面这个是SQL语句,到时我们再弄。
任何权限的需求,都是为广义的用户分配角色,角色拥有广义的权限。角色是最重要的中枢,隐藏做幕后黑手,从不出现在业务代码里,用行话说就是解除了用户和权限的直接耦合。
角色把用户抽象化了,几百个用户变成成几个角色,用户->角色->权限写成通用判断权限的方法:currUser.IsHave(xx权限)。核心就是一个sql联表查询语句,查询条件为用户id。
例如:
部门权限:部门也是一种用户,建立 部门表、部门角色表。通用权限方法里加上 当前部门->部门所属角色->权限
职位权限:职位也是一种用户,建立职位表、职位角色表,同上
菜单:也是一种权限,建立 菜单表、角色菜单表,就把菜单纳入了权限管理。通用权限方法里加上 角色列表->权限、菜单
一、五个表的模型
1.用户、角色,用户角色表都不用再建模
2.在Models下新建菜单表的模型MenuInfo.cs
using System.ComponentModel.DataAnnotations;
namespace MYtest2018.Models
{
public class MenuInfo
{
public int Id { get; set; }
[Display(Name = "菜单名称")]
public string MenuName { get; set; }
[Display(Name = "菜单地址")]
public string MenuPath { get; set; }
[Display(Name = "菜单图标")]
public string MenuIcon { get; set; }
/// <summary>
/// 为0则为父级菜单一级菜单,否则这里刚是他选择的上级菜单的ID值
/// </summary>
[Display(Name = "菜单图标")]
public int Popedomfatherid { get; set; }
[Display(Name = "排序")]
public int Sort { get; set; }
}
}
3。 在Models下新建菜单表的模型RoleMenu.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace MYtest2018.Models
{
public class RoleMenu
{
public int Id { get; set; }
[Display(Name = "角色ID")]
public string RoleId { get; set; }
[Display(Name = "菜单Id")]
public int MenuId { get; set; }
///(1为可用,2为不可用)
[Display(Name = "是否可用")]
public int IsAvailable { get; set; }
}
}
4,在上下文中引用好code frist建表。
在DAL下的MyDALContent上下文中,增加这二个表的引用
using System.Data.Entity;
namespace MYtest2018.DAL
{
public class MyDALContent : DbContext
{
/// <summary>
/// 连接
/// </summary>
public MyDALContent() : base("Mytest2018") { }
public System.Data.Entity.DbSet<MYtest2018.Models.Department> Departments { get; set; }
public System.Data.Entity.DbSet<MYtest2018.Models.RoleMenu> RoleMenus { get; set; }
public System.Data.Entity.DbSet<MYtest2018.Models.MenuInfo> MenuInfos { get; set; }
}
}
二。创建菜单管理列表
为了方便,我是删了数据,让他重新code first新建的表,我使用了我们上单的 install,自动建Super用户,同时就建好了所有的表表。
三、Account控制器中新建listMenu控制器并新建视图
效果图如下

在右边可以直接新建菜单不选择表示一级,选择了表示有二级,注意,因为使用bind,同时有模型验证,要是直接将一级菜单读成下拉是不行的,得给他新建一个不选择时为0的selected
var menus = db.MenuInfos.Where(m => m.Popedomfatherid == 0).OrderBy(x => x.Sort).Select(g => new SelectListItem
{
Text = g.MenuName,
Value = g.Id.ToString(),
Selected = false
}).ToList();
menus.Insert(0, new SelectListItem { Value = "0", Text = "请选择", Selected = true });
ViewBag.menuInfo = menus;
三、整个后台的源码
/// <summary>
/// 菜单列表
/// </summary>
/// <param name="disposing"></param>
[AllowAnonymous]
[HttpGet]
public ActionResult ListMenu(string SeachColumnString)
{
///读出所有的一级菜单
ViewBag.drolistmenu = db.MenuInfos.Where(m => m.Popedomfatherid == 0).OrderBy(x => x.Sort);
///读出所有的现有的菜单名称,以下拉框的形式写入。
var menus = db.MenuInfos.Where(m => m.Popedomfatherid == 0).OrderBy(x => x.Sort).Select(g => new SelectListItem
{
Text = g.MenuName,
Value = g.Id.ToString(),
Selected = false
}).ToList();
menus.Insert(0, new SelectListItem { Value = "0", Text = "请选择", Selected = true });
ViewBag.menuInfo = menus;
///下面是开始显示菜单的列表了
///读出所有的菜单列表
var data = db.MenuInfos.ToList();
///定义一个变量result的MenuInfo列表变量
var result = new List<MenuInfo>();
///读出所有的一级菜单
var level0 = data.Where(m => m.Popedomfatherid == 0).ToList();
///当一级菜单索引选择有的时候,一级菜单就仅为我们选择的这个
if (!string.IsNullOrEmpty(SeachColumnString))
{
var xxx = int.Parse(SeachColumnString);
level0 = level0.Where(m => m.Id == xxx).ToList();
}
///对所有一级菜单进行一次循环。 foreah (var xx in level0) 这种一样
level0.ForEach(item =>
{
///定义一个children字菜单变量,当他的Popedomfatherid=当前循环的ID,取出当前的所有字菜单
var children = data.Where(m => m.Popedomfatherid == item.Id).ToList();
///给子菜单名字前面加上几个---
children.ForEach(m => m.MenuName = "------------" + m.MenuName);
///为新定义的result变量增加一个 一级菜单
result.Add(item);
///为新定义的result变量增加一个 所有的字菜单
result.AddRange(children);
});
///ViewBag.list传值
ViewBag.List = result;
return View();
}
/// <summary>
/// 菜单列表
/// </summary>
/// <param name="disposing"></param>
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ListMenu([Bind(Include = "MenuName,MenuPath,MenuIcon,MethodName,ControllerName,Popedomfatherid,Sort")] MenuInfo model)
{
if (ModelState.IsValid)
{
db.MenuInfos.Add(model);
await db.SaveChangesAsync();
return RedirectToAction("ListMenu");
}
///读出所有的现有的菜单名称,以下拉框的形式写入。
var menus = db.MenuInfos.Where(m => m.Popedomfatherid == 0).OrderBy(x => x.Sort).Select(g => new SelectListItem
{
Text = g.MenuName,
Value = g.Id.ToString(),
Selected = false
}).ToList();
menus.Insert(0, new SelectListItem { Value = "0", Text = "请选择", Selected = true });
ViewBag.menuInfo = menus;
///读出所有的一级菜单
ViewBag.drolistmenu = db.MenuInfos.Where(m => m.Popedomfatherid == 0).OrderBy(x => x.Sort);
return View(model);
}
五、整个前台的源码
@using MYtest2018.Models
@model MenuInfo
@{
ViewBag.Title = "ListMenu";
Layout = "~/Views/Shared/_Layoutadmin.cshtml";
}
<div class="row wrapper border-bottom white-bg page-heading">
<div class="col-sm-4">
<h2>菜单管理</h2>
</div>
<div class="col-sm-8">
<div class="title-action">
</div>
</div>
</div>
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-8">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h3>一级菜单列表</h3>
<div class="form-group">
<div class="input-group">
<ul class="nav nav-pills nav-stacked" style="float:left;">
@foreach (MYtest2018.Models.MenuInfo data1 in (ViewBag.drolistmenu as IEnumerable<MYtest2018.Models.MenuInfo>))
{
<li class="titlesize" style="float:left;">
<a href="~/Account/ListMenu?SeachColumnString=@data1.Id"><span class="glyphicon glyphicon glyphicon-th-large" aria-hidden="true"></span> @data1.MenuName</a>
</li>
}
</ul>
</div>
</div>
</div>
<div class="ibox-content p-md">
@{
var data = ViewBag.List as List<MenuInfo>;
}
<div class="table-responsive">
<table class="table table-hover issue-tracker">
<tbody>
@if (data != null && data.Any())
{
foreach (var item in data)
{
<tr>
<td>
@item.Id
</td>
<td class="issue-info">
<i class="@item.MenuIcon"></i> @item.MenuName
</td>
<td>@item.ControllerName/@item.MethodName</td>
<td>
<a class="btn btn-xs btn-white" href="~/Account/DelMenuInfo/@item.Id" onclick="return confirm('你确认删除吗?')"><i class="fa fa-bank"></i> 删除 </a>
<a class="btn btn-xs btn-white" href="~/Account/EditMenuInfo/@item.Id"><i class="fa fa-edit"></i> 编辑</a>
</td>
</tr>
}
}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="ibox float-e-margins">
<div class="ibox-content p-md">
<h4 class="m-b-xxs">新建菜单</h4>
@using (Html.BeginForm("ListMenu", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary("", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(m => m.Popedomfatherid, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.DropDownList("Popedomfatherid", new SelectList(ViewBag.menuInfo, "Value", "Text"))
@*@Html.EditorFor(model => model.BossClass, new { htmlAttributes = new { @class = "form-control" } })*@
@Html.ValidationMessageFor(model => model.Popedomfatherid, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.MenuName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.MenuName, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.ControllerName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.ControllerName, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.MethodName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.MethodName, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.MenuIcon, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.MenuIcon, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.Sort, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.Sort, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-success" value="保存" />
</div>
</div>
}
</div>
</div>
</div>
</div>
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
浙公网安备 33010602011771号