zoukankan      html  css  js  c++  java
  • MVC5+EF6 --自定义控制Action访问权限

    本章主要讲解在MVC中灵活控制Action的访问权限;

    本章所使用的示例表也是上一张所使用的TbUser、TbRole、TbUserRole;

    最终的效果是针对任意一个Action或Controller,都可以根据配置的角色来控制访问权限;

    完成此核心功能后,可以再往两方面扩展常用功能:

    1. 可以根据 组织/用户/角色 的并集来控制权限

    2. 以此核心功能为基础,实现菜单的动态配置

    PS:扩展功能本章暂时不

    文章提纲

    • 概述
    • 理论基础
    • 详细步骤
    • 总结

    概述

    一、MVC Form认证身份基础

    通常用法举例:

    1. web.config配置,开启form认证

    2. 需要认证的 Control或Action 上添加过滤,例如限制只有用户"zhangsan"或者角色"管理员"可以访问

        

    另外还有一种常用形式表示:只要有用户登录就可以访问

    就是直接在Action或整个Controller上[Authorize]属性过滤

    二、为什么需要自定义MVC权限过滤器

    上述解决方式中很明显会发现有两个缺点:

    1. 修改权限时需在Action, Controller上修改后需重新编译,不灵活。

    2.过滤器中的Role是内置对象,如果不使用ASP.NET自身的集成权限方案,就无法按照角色来过滤。

    解决这两个问题,只需要扩展类AuthorizeAttribute即可。

    理论基础

    为了能使用自定义的角色控制权限,我们需要扩展或绕过 ASP.NET 的Membership和Role provider 框架。

    1.扩展:实现自定义的 Membership/Role provider

    2.绕过:直接不使用

     

    我们选择绕过的方式,这样的话更加灵活。

    (因为如果你的角色结构和系统不一致,用扩展的方式弄起来比较麻烦)

    我们使用form认证的三个核心API, 只用这几个API既可以减少工作量,又可以和Membership/Role provider保持独立。

    1. FormsAuthentication.SetAuthCookie

    用户登录后,指定用户名

    2. Request.IsAuthenticated

    登录后返回true

    3. HttpContext.Current.User.Identity.Name

    返回登录的用户名

     

    权限过滤的完整过程:

    1. Authetication ( 登录 )

    登录成功后,调用 FormsAuthentication.SetAuthCookie 设置一个用户名。

    2. Authorization(授权)

    新建自定义的授权属性类:CustomAuthorizeAttribute(继承于AuthorizeAtrribute),扩展权限过滤器

    3. 类似于默认Authorize attribute的使用方法,附加自定义的authorize attribute到controller或action上去,实现权限过滤

    详细步骤

    一、启用form认证,完成登录/退出 基本功能

    1. 启用 form 认证

    配置web.config,启用form认证

    2. 完成登录/退出 基本功能

    新建Controller: AccountController.cs

        public class AccountController : Controller
        {
            private MyDbContext db = new MyDbContext();
            // GET: Account
            public ActionResult Index()
            {
                return View();
            }
    
            public ActionResult Login()
            {
                ViewBag.LoginState = "登录前...";
                TempData["returnUrl"] = Request["ReturnUrl"];
                //如果当前有登录用户,就需要跳转到权限不足提示页面
                if (!string.IsNullOrEmpty(User.Identity.Name))
                {
                    return View("NoPermissions");
                }
                else
                {
                    return View();
                }
            }
            [HttpPost]
            public ActionResult Login(TbUser user)
            {
                string url = Convert.ToString(TempData["returnUrl"]);
                var userinfo = db.TbUsers.FirstOrDefault(u => u.Email == user.Email && u.Password == user.Password);
                if (userinfo != null)
                {
                    FormsAuthentication.SetAuthCookie(userinfo.UserName, false);
                    if (!string.IsNullOrEmpty(url))
                    {
                        ViewBag.LoginState = userinfo.UserName + "登录后...";
                        return Redirect(url);
                    }
                    else
                    {
                        return Redirect("~/");
                    }
    
                }
                else
                {
                    ViewBag.LoginState = user.Email + "用户不存在...";
                }
                return View();
            }
    
            public ActionResult Logout()
            {
                FormsAuthentication.SignOut();
                return Redirect(Request.UrlReferrer.ToString());
            }
            /// <summary>
            /// 权限不足页面
            /// </summary>
            /// <returns></returns>
            public ActionResult NoPermissions()
            {
                return View();
            }
    
        }
    View Code

    登录功能:

    登出功能:

    这里对应的视图文件就不展示了

    二、准备好权限配置文件

    1. 用到的基础数据:用户,角色及用户/角色 关系

    2. 角色与Action对应的权限关系

    这里我们先用一个xml代替,理解这个后就可以扩展到DB中。

    新建文件夹Config,新建ActionRoles文件,配置Action/Role的对应关系

    PS:

    Action未配置情况下,默认有访问权限;

    Action 配置角色为空,有访问权限。

    三、扩展 AuthorizeAttribute

    1. 新建类CustomAuthorizeAttribute,继承与AuthorizeAttribute

    override两个方法:

    在请求授权时调用:

    提供一个入口点用于自定义授权检查,通过为true

    CustomAuthorizeAttribute.cs(两个方法的具体实现):

        public class CustomAuthorizeAttribute : AuthorizeAttribute
        {
            private MyDbContext db = new MyDbContext();
    
            /// <summary>
            /// 对应Action允许的角色
            /// </summary>
            private string[] AuthRoles { get; set; }
            /// <summary>
            /// 在请求授权时调用
            /// </summary>
            /// <param name="httpContext"></param>
            /// <returns></returns>
            protected override bool AuthorizeCore(HttpContextBase httpContext)
            {
                if (httpContext == null)
                {
                    throw new ArgumentNullException("HttpContext");
                }
                if (AuthRoles == null || AuthRoles.Length == 0)
                {
                    return false;
                }
                #region 确定当前用户角色是否属于指定的角色
                //获取当前用户所在角色
                string sql = "select RoleName from TbRole where Id in(select roleId "
                            + "from TbUserRole where userid = "
                            + "(select id from TbUser where UserName=@UserName))";
                string currentUser = httpContext.User.Identity.Name;
                SqlParameter[] paras = new SqlParameter[] { 
                    new SqlParameter("@UserName",currentUser)
                };
                var userRoles = db.Database.SqlQuery<string>(sql, paras).ToList();
                //验证是否属于对应角色
                for (int i = 0; i < AuthRoles.Length; i++)
                {
                    if (userRoles.Contains(AuthRoles[i]))
                    {
                        return true;
                    }
                }
                #endregion
                return false;
            }
            /// <summary>
            /// 提供一个入口点用于自定义授权检查,通过为true
            /// </summary>
            /// <param name="filterContext"></param>
            public override void OnAuthorization(AuthorizationContext filterContext)
            {
                string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
                string actionName = filterContext.ActionDescriptor.ActionName;
                //获取config文件配置的action允许的角色,以后可以转到数据库中
                string roles = GetActionRoles(actionName, controllerName);
                if (!string.IsNullOrWhiteSpace(roles))
                {
                    this.AuthRoles = roles.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                }
                else {
                    this.AuthRoles = new string[] { };
                }
                base.OnAuthorization(filterContext);
            }
            /// <summary>
            /// 根据当前Controller和Action名称获取对应节点内容
            /// </summary>
            /// <param name="action">Action名称</param>
            /// <param name="controller">Controller名称</param>
            /// <returns></returns>
            private static string GetActionRoles(string action, string controller)
            {
                XElement rootElement = XElement.Load(HttpContext.Current.Server.MapPath("~/Configs/") + "ActionRoles.xml");
                XElement controllerElement = FindElementByAttribute(rootElement, "Controller", controller);
                if (controllerElement != null)
                {
                    XElement actionElement = FindElementByAttribute(controllerElement, "Action", action);
                    if (actionElement != null)
                    {
                        return actionElement.Value;
                    }
                }
                return "";
            }
    
            private static XElement FindElementByAttribute(XElement xelement, string tagName, string attribute)
            {
                XElement nowXelement = xelement.Elements(tagName).FirstOrDefault(e => e.Attribute("name").Value.Equals(attribute, StringComparison.OrdinalIgnoreCase));
                return nowXelement;
            }
        }
    View Code

    总结

    在此,权限控制的整个过程就差不多了,我们来测试一下。

    新建HomeController, 新建一些Action做测试(Index, About,Install,Other)

    回顾一下基础数据。

    HomeController.cs

    namespace TestMVC.Controllers
    {
        [CustomAuthorizeAttribute]
        public class HomeController : Controller
        {
            // GET: Home
            public ActionResult Index()
            {
                return View();
            }
    
            public ActionResult About()
            {
                return View();
            }
            public ActionResult Install()
            {
                return View();
            }
            public ActionResult Other()
            {
                return View();
            }
        }
    }
    View Code

    对应视图:

    补充说明:

    如下图,可以设置为全局。

    这样就不需要单个设置,对所有Action应用自定义过滤条件。

     源码下载

  • 相关阅读:
    [考试反思]1008csp-s模拟测试65:突袭
    [考试反思]1008csp-s模拟测试64:契机
    [考试反思]1007csp-s模拟测试63:朦胧
    [考试反思]1006csp-s模拟测试62:隔断
    [考试反思]1005csp-s模拟测试61:休止
    [毒瘤题]玛里苟斯:线性基,表达式分析,测试点分治
    albus就是要第一个出场:线性基
    [考试反思]1005csp-s模拟测试60:招魂
    [考试反思]1004csp-s模拟测试59:惊醒
    [考试反思]1003csp-s模拟测试58:沉淀
  • 原文地址:https://www.cnblogs.com/myindex/p/5479428.html
Copyright © 2011-2022 走看看