zoukankan      html  css  js  c++  java
  • MVC+EF6教程六:灵活控制Action权限

    本篇专题主要讲述MVC中的权限方案。

    权限控制是每个系统都必须解决的问题。

    前面的系列文章中我们用到了 SysUser, SysRole, SysUserRole 这几个示例表。

    我们以此为基础,完成RBAC (基于角色的控制) 的核心功能。

    在此给出我的最佳实践,最终的效果是针对任意一个Action或Controller,都可以根据配置的角色来控制访问权限。

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

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

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

    文章提纲

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

    概述要点(知识点)

    一、MVC Form认证身份基础

    通常用法举例:

    1. web.config 》 system.web配置节下,开启form认证

      <system.web>
        <authentication mode="Forms">
          <forms loginUrl="~/Account/Login" timeout="10080" cookieless="UseCookies" name="LoginCookieName"></forms>
        </authentication>
      </system.web>

    2. 需要认证的 Control或Action 上添加过滤,例如限制只有 Scott可以访问

    [Authorize(Users = "Scott")]
    public ActionResult Index()
    {
      return View();
    }

    还有其他两种常用形式,分别表示:

    登录用户可以访问

    [Authorize]

    角色为Admin的用户可以访问

    [Authorize(Roles = "Admin")]

    过滤条件可以加在Action或整个Controller上。

    二、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 》 system.web配置节下,启用form认证

      <system.web>
        <authentication mode="Forms">
          <forms loginUrl="~/Account/Login" timeout="10080" cookieless="UseCookies" name="LoginCookieName"></forms>
        </authentication>
        <compilation debug="true" targetFramework="4.7.2" />
        <httpRuntime targetFramework="4.7.2" />
      </system.web>

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

    新建Controller: AccountController

    登录功能:

    public ActionResult Login()
    {
      FormsAuthentication.SignOut();
      TempData["ReturnUrl"] = Convert.ToString(Request["ReturnUrl"]);
      return View();
    }
       public ActionResult Login(FormCollection fc)
       {
                //1.获取表单数据
                string userName = fc["UserName"];
                string password = fc["Password"];
                string email = fc["Email"];
                bool remenber = fc["ckbRemenber"]==null?false:true;
                string returnUrl= Convert.ToString(TempData["ReturnUrl"]);
                //2.验证用户
                SysUser user = work.SysUserRepository.Get(filter: u => u.UserName == userName & u.Password == password).FirstOrDefault();
                work.Dispose();
                //3.保存票据
                if (user!=null)
                {
                    FormsAuthentication.SetAuthCookie(userName, remenber);
                    if (!string.IsNullOrEmpty(returnUrl))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                       
                        return Redirect("~/");
                    }
                }
                else
                {
                    ViewBag.LoginState = "用户名或密码错误,请重试!";
                }
    
                return View();
       }

    退出功能

    public ActionResult LoginOut()
    {
      FormsAuthentication.SignOut();
      return View();
    }

    二、准备好权限配置文件

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

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

    这里我们先用一个XML代替,后续最终项目完成时会统一到DB中。

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

    <?xml version="1.0" encoding="utf-8" ?>
    <Roles>
      <Controller name="Home">
        <Action name="Index">Test</Action>
        <Action name="About">Manager,Admin,General Users</Action>
        <Action name="Contact">Admin</Action>
      </Controller>
    </Roles>

    说明:

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

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

    三、扩展 AuthorizeAttribute

    1. 新建类CustomAuthorizeAttribute,继承与AuthorizeAttribute

    override两个方法:

    a. 在请求授权时调用:

    public virtual void OnAuthorization(AuthorizationContext filterContext);

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

    protected virtual bool AuthorizeCore(HttpContextBase httpContext);

    具体实现:

      public class CustomAuthorizeAttribute : AuthorizeAttribute
        {
            AccountContext db = new AccountContext();
            private string[] AuthRoles { get; set; }
            public override void OnAuthorization(AuthorizationContext filterContext)
            {
                // 获取控制器名称
                string controlName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
                // 获取操作方法名称
                string actionName = filterContext.ActionDescriptor.ActionName;
                // 读取XML
                string roles = GetXMLRoles.GetActionRoles(actionName, controlName);
                if (!string.IsNullOrWhiteSpace(roles))
                {
                    this.AuthRoles = roles.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                }
                else
                {
                    this.AuthRoles = new string[] { };
                }
                base.OnAuthorization(filterContext);
            }
    
            protected override bool AuthorizeCore(HttpContextBase httpContext)
            {
                if (httpContext == null)
                {
                    throw new ArgumentNullException("HttpContext");
                }
                if (AuthRoles == null || AuthRoles.Length == 0)
                {
                    return true;
                }
                if (!httpContext.User.Identity.IsAuthenticated)
                {
                    return false;
                }
                // 1.确定当前角色是否属于指定的角色
                string query = @"select RoleName from SysRole where ID in 
                                (select SysRoleID from SysUserRole where SysUserID in
                                (select ID from SysUser where UserName=@UserName))";
                string currentName = httpContext.User.Identity.Name;
                SqlParameter[] paras = new SqlParameter[]
                {
                    new SqlParameter("@UserName",currentName)
                };
                var userRoles = db.Database.SqlQuery<string>(query, paras).ToList();
                // 2.验证是否属于AuthRoles
                foreach (var authRole in AuthRoles)
                {
                    if (userRoles.Contains(authRole))
                    {
                        return true;
                    }
                }
                return false;
            }
    
        }

    以上使用的GetActionRoles的实现:

     public class GetXMLRoles
        {
            /// <summary>
            /// 读取XML文件
            /// </summary>
            /// <param name="action"></param>
            /// <param name="controller"></param>
            /// <returns></returns>
            public static string GetActionRoles(string action, string controller)
            {
                XElement rootElement = XElement.Load(HttpContext.Current.Server.MapPath("~/Config/") + "ActionRoles.xml");
                XElement controlElement = FindElementByAttribute(rootElement, "Controller", controller);
                if (controlElement != null)
                {
                    XElement actionElement = FindElementByAttribute(controlElement, "Action", action);
                    if (actionElement != null)
                    {
                        return actionElement.Value;
                    }
                }
                return "";
            }
            /// <summary>
            /// 读取XML文件指定节点
            /// </summary>
            /// <param name="xElement"></param>
            /// <param name="tagName"></param>
            /// <param name="attribute"></param>
            /// <returns></returns>
            public static XElement FindElementByAttribute(XElement xElement, string tagName, string attribute)
            {
                return xElement.Elements(tagName).FirstOrDefault(x => x.Attribute("name").Value.Equals(attribute, StringComparison.OrdinalIgnoreCase));
            }
        }

    总结

    至此,权限控制的整个过程就OK了,我们来测试一下。

    我们把整个HomeController 上都加上 [CustomAuthorize]

    使用场景举例:

    1. Index 配置为空,任何人都能访问

    2. About 配置为Manager, Administrators, General Users 可以访问

    未登录时跳转至登录界面

    3. Contact配置为 Administrators可以访问

    scott的角色为General Users, 不在权限表里,登录不进去此页面

    另外补充说明:

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

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

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

    权责申明

    作者:编程小纸条 出处: https://www.cnblogs.com/miro/category/620362.html

  • 相关阅读:
    前端性能优化方法
    软件测试十大原则
    CSRF攻击【转载】
    XSS攻击(出现的原因、预防措施......)
    python 连接MySQL数据库
    《快消品营销人的第一本书》
    python 格式化输出日志记录
    python 安装cv2
    测试用例设计的策略
    【转】APP功能测试要领
  • 原文地址:https://www.cnblogs.com/qianj/p/12516879.html
Copyright © 2011-2022 走看看