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

本篇主要讲述在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;
MVC 基于 AuthorizeAttribute 实现的登陆权限控制_第1张图片
还是在App_Start目录下找到 FilterConfig类,添加注册。

public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
            filters.Add(new UserAuthAttribute());//注册
        }
    }

现在已经完成了基本的架构。

系统要有用户,角色,根据登陆的用户获取角色,再根据角色获取可访问的页面。 我这里用简单的表结构描述各表之间的关系。
MVC 基于 AuthorizeAttribute 实现的登陆权限控制_第2张图片

在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.

这些部分完成后,接下来我们要创建登陆页面和登陆后显示的主页,以及子页面,还有无权访问提示页。
MVC 基于 AuthorizeAttribute 实现的登陆权限控制_第3张图片

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积分呢。

你可能感兴趣的:(C#)