您的位置:首页 > 其它

MVC 基于 AuthorizeAttribute 实现的登陆权限控制

2016-12-16 16:54 211 查看
本篇主要讲述在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<User> Users
{
get
{
//模拟用户
List<User> lists = new List<User>();
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;
}
}
/// <summary>
/// 页面
/// </summary>
public static List<SysMenu> SysMenuList
{
get
{
List<SysMenu> lists = new List<SysMenu>();

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;
}
}

/// <summary>
/// 角色
/// </summary>
public static List<Role> RoleList
{
get
{
List<Role> roles = new List<Role> { new Role { RoleID = 1, RoleName = "Admin" }, new Role { RoleID = 2, RoleName = "User" } };
return roles;
}
}
/// <summary>
/// 角色可访问的页面
/// </summary>
public static List<RoleMenu> RoleMenuList
{
get
{
List<RoleMenu> roles = new List<RoleMenu> {
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<UserRole> UserRoleList
{
get
{
return new List<UserRole>()
{
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<SysMenu> 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页前台,

<!DOCTYPE html>

<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>LogIn</title>
</head>
<body>
<div style=" width:600px; margin:50px auto;">
<form action="/Account/LogIn" method="post">
<table>
<tr><td>User Name</td><td><input type="text" id="username" name="username" /></td></tr>
<tr><td>Pass word</td><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<User> 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<SysMenu> MenuList = httpContext.Session[WebConstants.UserRoleMenu] as List<SysMenu>;

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源码, 我感觉能不动组件代码就别动, 如果版本更新,换个人维护谁知道你改了组件。

有没有更好的方法,通用的,一看就知道在哪的办法呢?

希望大家给些建议。

实例代码在百度网盘 http://pan.baidu.com/s/1o8O19ZO

我才不去赚CSDN积分呢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息