本篇主要讲述在MVC中使用AuthorizeAttribute实现登陆和权限控制,通俗的讲就是,判定用户是否登陆,若没登陆就跳转到登陆页,是否有权限访问页面,若没有则跳转到特定提示页。
网上有很多类似的,我也借鉴了部分,如有雷同请见谅。
关于AuthorizeAttribute的详解 请查看 https://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute(v=vs.118).aspx
代码的执行顺序是 OnAuthorization–>AuthorizeCore–>HandleUnauthorizedRequest 不过我去掉了OnAuthorization部分。这部分也可以做很多验证的。如果AuthorizeCore返回false时,才会走HandleUnauthorizedRequest 方法,并且Request.StausCode会返回401。
首先创建一个MCV的项目,在App_Start目录下创建一个类UserAuthAttribute,此类要继承AuthorizeAttribute类,这里继承的时候注意using System.Web.Mvc;
还是在App_Start目录下找到 FilterConfig类,添加注册。
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new UserAuthAttribute());//注册
}
}
现在已经完成了基本的架构。
系统要有用户,角色,根据登陆的用户获取角色,再根据角色获取可访问的页面。 我这里用简单的表结构描述各表之间的关系。
在Models文件下创建各实体。(可以用edmx直接生成的,然而我没有)
public class User
{
public int UserID { get; set; }
public string UserName { get; set; }
public string PassWord { get; set; }
}
public class Role
{
public int RoleID { get; set; }
public string RoleName { get; set; }
}
public class UserRole
{
public int UserRoleID { get; set; }
public int UserID { get; set; }
public int RoleID { get; set; }
}
public class SysMenu
{
public int MenuID { get; set; }
public string MenuURL { get; set; }
public string MenuName { get; set; }
}
public class RoleMenu
{
public int RoleMenuID { get; set; }
public int RoleID { get; set; }
public int MenuID { get; set; }
}
然后我们要创建一些模拟数据
public class TestData
{
public static List Users
{
get
{
//模拟用户
List lists = new List();
lists.Add(new User { UserID = 1, UserName = "test1", PassWord = "1" });
lists.Add(new User { UserID = 2, UserName = "test2", PassWord = "2" });
lists.Add(new User { UserID = 3, UserName = "test3", PassWord = "3" });
lists.Add(new User { UserID = 4, UserName = "test4", PassWord = "4" });
return lists;
}
}
///
/// 页面
///
public static List SysMenuList
{
get
{
List lists = new List();
lists.Add(new SysMenu { MenuID = 0, MenuName = "Home", MenuURL = "/Account/Home" });
lists.Add(new SysMenu { MenuID = 1, MenuName = "Index", MenuURL = "/Admin/Index" });
lists.Add(new SysMenu { MenuID = 2, MenuName = "TestPageOne", MenuURL = "/Admin/TestPageOne" });
lists.Add(new SysMenu { MenuID = 3, MenuName = "TestPageTwo", MenuURL = "/Admin/TestPageTwo" });
lists.Add(new SysMenu { MenuID = 4, MenuName = "TestPageThree", MenuURL = "/Admin/TestPageThree" });
lists.Add(new SysMenu { MenuID = 5, MenuName = "Index", MenuURL = "/Test/Index" });
lists.Add(new SysMenu { MenuID = 6, MenuName = "TestPageOne", MenuURL = "/Test/TestPageOne" });
lists.Add(new SysMenu { MenuID = 7, MenuName = "TestPageTwo", MenuURL = "/Test/TestPageTwo" });
lists.Add(new SysMenu { MenuID = 8, MenuName = "TestPageThree", MenuURL = "/Test/TestPageThree" });
return lists;
}
}
///
/// 角色
///
public static List RoleList
{
get
{
List roles = new List { new Role { RoleID = 1, RoleName = "Admin" }, new Role { RoleID = 2, RoleName = "User" } };
return roles;
}
}
///
/// 角色可访问的页面
///
public static List RoleMenuList
{
get
{
List roles = new List {
new RoleMenu { RoleMenuID=1, RoleID = 1,MenuID = 1 },
new RoleMenu { RoleMenuID=2, RoleID = 1,MenuID = 2 },
new RoleMenu { RoleMenuID=3, RoleID = 1,MenuID = 3 },
new RoleMenu { RoleMenuID=4, RoleID = 1,MenuID = 4 },
new RoleMenu { RoleMenuID=5, RoleID = 2,MenuID = 5 },
new RoleMenu { RoleMenuID=6, RoleID = 2,MenuID = 6 },
new RoleMenu { RoleMenuID=7, RoleID = 2,MenuID = 7 },
new RoleMenu { RoleMenuID=8, RoleID = 2,MenuID = 8 },
new RoleMenu { RoleMenuID=9, RoleID = 1,MenuID = 0 },
new RoleMenu { RoleMenuID=10, RoleID = 2,MenuID =0 },
};
return roles;
}
}
public static List UserRoleList
{
get
{
return new List()
{
new UserRole{ UserRoleID=1,UserID=1,RoleID=1},
new UserRole{ UserRoleID=2,UserID=1,RoleID=2},
new UserRole{ UserRoleID=3,UserID=2,RoleID=1},
new UserRole{ UserRoleID=4,UserID=3,RoleID=2},
new UserRole{ UserRoleID=5,UserID=4,RoleID=2}
};
}
}
public static List GetMenuByUserID(int UserID)
{
//var usermenu = from u in Users
// where u.UserID == UserID
// join ur in UserRoleList on u.UserID equals ur.UserID
// join r in RoleList on ur.RoleID equals r.RoleID
// join rm in RoleMenuList on r.RoleID equals rm.RoleID
// join m in SysMenuList on rm.MenuID equals m.MenuID
// select new
// {
// u.UserID,
// u.UserName,
// r.RoleID,
// r.RoleName,
// m.MenuID,
// m.MenuURL
// };
var usermenu = from u in Users
where u.UserID == UserID
join ur in UserRoleList on u.UserID equals ur.UserID
join r in RoleList on ur.RoleID equals r.RoleID
join rm in RoleMenuList on r.RoleID equals rm.RoleID
join m in SysMenuList on rm.MenuID equals m.MenuID
select m;
return usermenu.Distinct().ToList();
}
}
其中GetMenuByUserID(int UserID) 方法是根据UserID获取可访问的Menu.
这些部分完成后,接下来我们要创建登陆页面和登陆后显示的主页,以及子页面,还有无权访问提示页。
LogIn页前台,
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>LogIntitle>
head>
<body>
<div style=" width:600px; margin:50px auto;">
<form action="/Account/LogIn" method="post">
<table>
<tr><td>User Nametd><td><input type="text" id="username" name="username" />td>tr>
<tr><td>Pass wordtd><td><input type="password" id="password" name="password" />td>tr>
<tr><td>td><td><input type="submit" value="LogIn" />td>tr>
table>
form>
div>
body>
html>
LogIn后台
[AllowAnonymous]
public ActionResult LogIn()
{
string User_Name = this.Request.Form["username"];
string User_Pw = this.Request.Form["password"];
if (!string.IsNullOrEmpty(User_Name) && !string.IsNullOrEmpty(User_Pw))
{
List Ulist = TestData.Users;
var userinfos = Ulist.Where(e => e.UserName.Equals(User_Name) && e.PassWord.Equals(User_Pw));
if (userinfos != null && userinfos.Count() == 1)
{
User _user = userinfos.FirstOrDefault();
Session[WebConstants.UserSession] = _user;
Session[WebConstants.UserRoleMenu] = TestData.GetMenuByUserID(_user.UserID);
string fromurl = Request.UrlReferrer.Query;
if (fromurl.IndexOf("?fromurl=") > -1)
{
fromurl = fromurl.Substring(9);
return this.Redirect(fromurl);
}
else
{
return this.RedirectToAction("Home", "Account");
}
}
}
return View();
}
注意:这里加了标识 [AllowAnonymous] ,表示允许任何用户访问,NoPremission 也要加。
[AllowAnonymous]
public ActionResult NoPremission()
{
return View();
}
登陆完成后,session记录用户信息和可访问的Menu信息,跳转到主页或者先前页。
现在已经完成大部分了。but 最主要的还没做,就是之前创建的UserAuthAttribute这个类。
在类里先定义个变量
public bool IsLogin = false;
开始最主要部分。验证是否已经登陆,判定是否有权限
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool Pass = false;
try
{
var websession = httpContext.Session[WebConstants.UserSession];
if (websession == null)
{
httpContext.Response.StatusCode = 401;//无权限状态码
Pass = false;
IsLogin = false;
}
else
{
User user = httpContext.Session[WebConstants.UserSession] as User;
if (user == null)
{
httpContext.Response.StatusCode = 401;//无权限状态码
Pass = false;
IsLogin = false;
}
else if (!IsMenuRole(httpContext))
{
httpContext.Response.StatusCode = 401;//无权限状态码
Pass = false;
IsLogin = true;
}
else
{
Pass = true;
}
}
}
catch (Exception)
{
return Pass;
}
return Pass;
}
返回false的处理, 进行跳转, 若没登陆,跳转到登陆页并带有参数,当登陆完成后可以跳转的先前页。这URL可以使用加密,防止客户修改或传递的参数发生编码错误。
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
else
{
if (!IsLogin)
{
string fromUrl = filterContext.HttpContext.Request.Url.PathAndQuery;
// string strUrl = new UrlHelper(filterContext.RequestContext).Action("Login", "Account","") + "?fromurl={0}";
string strUrl = "~/Account/Login/?fromurl={0}";
filterContext.HttpContext.Response.Redirect(string.Format(strUrl, fromUrl), true);
}
else
{
filterContext.Result = new RedirectResult("~/Account/NoPremission");
}
}
}
判定是否有权限, URL可能带有参数,我这里使用了GUID模拟的,
如果参数很多,可以写个类特殊处理下,
public bool IsMenuRole(HttpContextBase httpContext)
{
string rawurll = httpContext.Request.RawUrl.ToLower();
List MenuList = httpContext.Session[WebConstants.UserRoleMenu] as List;
Guid guid;
//这里是过滤掉 url后的GUID参数
int rindex = rawurll.LastIndexOf("/");
if (rindex > 0 && Guid.TryParse(rawurll.Substring(rindex + 1), out guid))
{
rawurll = rawurll.Substring(0, rindex);
}
if (MenuList.Where(e => e.MenuURL.ToLower().Contains(rawurll)).Count() == 0)
{
return false;
}
return true;
}
现在已经完成了。
现在我有些疑问。
1我发现这个只是在访问页面的时候能起作用, 如果是ajax请求根本就不跳转, 我用bootstrap+dataTable 做个列表, 如果登陆超时,点搜索按钮时 就报错,不跳转到登陆页。 我好多页面都是这么做的,每个页面都加js判定会累死的, 有人说去改dataTable源码, 有人说去改ajax源码, 我感觉能不动组件代码就别动, 如果版本更新,换个人维护谁知道你改了组件。
有没有更好的方法,通用的,一看就知道在哪的办法呢?
希望大家给些建议。
实例代码在百度网盘 https://pan.baidu.com/s/1tPz9im85P7o0NZ9MgFW21Q
我才不去赚CSDN积分呢。