MVC5+EF6 --自定义控制Action访问权限

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

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

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

完成此核心功能后,能够再往两方面扩展经常使用功能:数据库

1. 能够根据 组织/用户/角色 的并集来控制权限框架

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

PS:扩展功能本章暂时不测试

文章提纲

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

概述

1、MVC Form认证身份基础this

一般用法举例:url

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

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

    

另外还有一种经常使用形式表示:只要有用户登陆就能够访问

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

2、为何须要自定义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上去,实现权限过滤

详细步骤

1、启用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

登陆功能:

登出功能:

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

2、准备好权限配置文件

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

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

这里咱们先用一个xml代替,理解这个后就能够扩展到DB中。

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

PS:

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

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

 

3、扩展 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应用自定义过滤条件。

 源码下载

相关文章
相关标签/搜索