zoukankan      html  css  js  c++  java
  • MVC过滤器:自定义授权过滤器

    一、授权过滤器

    授权过滤器用于实现IAuthorizationFilter接口和做出关于是否执行操作方法(如执行身份验证或验证请求的属性)的安全策略。AuthorizeAttribute类继承了IAuthorizationFilter接口,是授权过滤器的示例。授权过滤器在任何其他过滤器之前运行。

    如果要自定义授权过滤器,只需要定义一个类继承自AuthorizeAttribute类,然后重写AuthorizeAttribute类里面的方法即可。

    二、示例

    下面根据一个具体的案例来讲解如何使用自定义过滤器

    1、添加对应实体类

    User实体类代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace MVCCustomerFilterDemo.Models
    {
        public class User
        {
            public int Id { get; set; }
            public string UserName { get; set; }
            public int RoleId { get; set; }
        }
    }

    Role实体类代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace MVCCustomerFilterDemo.Models
    {
        public class Role
        {
            public int Id { get; set; }
            public string RoleName { get; set; }
            public string Description { get; set; }
        }
    }

     RoleWithControllerAction实体类代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace MVCCustomerFilterDemo.Models
    {
        public class RoleWithControllerAction
        {
            public int Id { get; set; }
            public string ControllerName { get; set; }
            public string ActionName { get; set; }
            public string RoleIds { get; set; }
        }
    }

    用于展示登录视图的登录用户实体类LogOnViewModel代码如下:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Web;
    
    namespace MVCCustomerFilterDemo.Models
    {
        // <summary>
        /// 用户登录类
        /// </summary>
        public class LogOnViewModel
        {
            /// <summary>
            /// 用户名
            /// </summary>
            [DisplayName("用户名")]
            public string UserName { get; set; }
    
            /// <summary>
            /// 密码
            /// </summary>
            [DisplayName("密码")]
            public string Password { get; set; }
    
            /// <summary>
            /// 记住我
            /// </summary>
            [DisplayName("记住我")]
            public bool RememberMe { get; set; }
    
        }
    }

    2、添加测试数据

    在程序中模拟数据库中的数据,实际使用中要去数据库查询,代码如下:

    using MVCCustomerFilterDemo.Models;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace MVCCustomerFilterDemo.DataBase
    {
        /// <summary>
        /// 测试数据(实际项目中,这些数据应该从数据库拿)
        /// </summary>
        public class SampleData
        {
            public static List<User> users;
            public static List<Role> roles;
            public static List<RoleWithControllerAction> roleWithControllerAndAction;
    
            static SampleData()
            {
                // 初始化用户
                users = new List<User>()
                {
                    new User(){ Id=1, UserName="jxl", RoleId=1},
                    new User(){ Id=2, UserName ="senior1", RoleId=2},
                    new User(){ Id=3, UserName ="senior2", RoleId=2},
                    new User(){ Id=5, UserName="junior1", RoleId=3},
                    new User(){ Id=6, UserName="junior2", RoleId=3},
                    new User(){ Id=6, UserName="junior3", RoleId=3}
                };
                // 初始化角色
                roles = new List<Role>()
                {
                    new Role() { Id=1, RoleName="管理员", Description="管理员角色"},
                    new Role() { Id=2, RoleName="高级会员", Description="高级会员角色"},
                    new Role() { Id=3, RoleName="初级会员", Description="初级会员角色"}
                };
                // 初始化角色控制器和Action对应类
                roleWithControllerAndAction = new List<RoleWithControllerAction>()
                {
                    new RoleWithControllerAction(){ Id=1, ControllerName="AuthFilters", ActionName="AdminUser", RoleIds="1"},
                    new RoleWithControllerAction(){ Id=2, ControllerName="AuthFilters", ActionName="SeniorUser",RoleIds="1,2"},
                    new RoleWithControllerAction(){ Id=3, ControllerName="AuthFilters", ActionName="JuniorUser",RoleIds="1,2,3"},
                    new RoleWithControllerAction(){ Id=3, ControllerName="AuthFilters", ActionName="Welcome",RoleIds="1,2"},
                    new RoleWithControllerAction(){ Id=4, ControllerName="ActionFilters", ActionName="Index", RoleIds="2,3"},
                    new RoleWithControllerAction(){ Id=4, ControllerName="ActionPremisFilters", ActionName="Index", RoleIds="2,3"}
                };
            }
        }
    }

    3、新建继承类

    新建一个UserAuthorize类,继承自AuthorizeAttribute类,然后F12转到定义查看AuthorizeAttribute代码,代码如下:

    #region 程序集 System.Web.Mvc, Version=5.2.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
    // E:Practice过滤器自定义权限过滤器MVCCustomerFilterDemopackagesMicrosoft.AspNet.Mvc.5.2.4lib
    et45System.Web.Mvc.dll
    #endregion
    
    namespace System.Web.Mvc
    {
        //
        // 摘要:
        //     指定对控制器或操作方法的访问只限于满足授权要求的用户。
        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
        public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
        {
            //
            // 摘要:
            //     初始化 System.Web.Mvc.AuthorizeAttribute 类的新实例。
            public AuthorizeAttribute();
    
            //
            // 摘要:
            //     获取或设置有权访问控制器或操作方法的用户角色。
            //
            // 返回结果:
            //     有权访问控制器或操作方法的用户角色。
            public string Roles { get; set; }
            //
            // 摘要:
            //     获取此特性的唯一标识符。
            //
            // 返回结果:
            //     此特性的唯一标识符。
            public override object TypeId { get; }
            //
            // 摘要:
            //     获取或设置有权访问控制器或操作方法的用户。
            //
            // 返回结果:
            //     有权访问控制器或操作方法的用户。
            public string Users { get; set; }
    
            //
            // 摘要:
            //     在过程请求授权时调用。
            //
            // 参数:
            //   filterContext:
            //     筛选器上下文,它封装有关使用 System.Web.Mvc.AuthorizeAttribute 的信息。
            //
            // 异常:
            //   T:System.ArgumentNullException:
            //     filterContext 参数为 null。
            public virtual void OnAuthorization(AuthorizationContext filterContext);
            //
            // 摘要:
            //     重写时,提供一个入口点用于进行自定义授权检查。
            //
            // 参数:
            //   httpContext:
            //     HTTP 上下文,它封装有关单个 HTTP 请求的所有 HTTP 特定的信息。
            //
            // 返回结果:
            //     如果用户已经过授权,则为 true;否则为 false。
            //
            // 异常:
            //   T:System.ArgumentNullException:
            //     httpContext 参数为 null。
            protected virtual bool AuthorizeCore(HttpContextBase httpContext);
            //
            // 摘要:
            //     处理未能授权的 HTTP 请求。
            //
            // 参数:
            //   filterContext:
            //     封装有关使用 System.Web.Mvc.AuthorizeAttribute 的信息。filterContext 对象包括控制器、HTTP 上下文、请求上下文、操作结果和路由数据。
            protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext);
            //
            // 摘要:
            //     在缓存模块请求授权时调用。
            //
            // 参数:
            //   httpContext:
            //     HTTP 上下文,它封装有关单个 HTTP 请求的所有 HTTP 特定的信息。
            //
            // 返回结果:
            //     对验证状态的引用。
            //
            // 异常:
            //   T:System.ArgumentNullException:
            //     httpContext 参数为 null。
            protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext);
        }
    }

     从AuthorizeAttribute的源代码中可以看出:里面定义了Users和Roles两个属性,只需要给这两个属性赋值,就可以控制用户或角色访问了。要实现自定义的验证只需要重写OnAuthorization和AuthorizeCore方法。所以,UserAuthorize类代码如下:

    using MVCCustomerFilterDemo.DataBase;
    using MVCCustomerFilterDemo.Models;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    
    namespace MVCCustomerFilterDemo.Extensions
    {
        public class UserAuthorize : AuthorizeAttribute
        {
            /// <summary>
            /// 授权失败时呈现的视图
            /// </summary>
            public string AuthorizationFailView { get; set; }
    
            /// <summary>
            /// 请求授权时执行
            /// </summary>
            public override void OnAuthorization(AuthorizationContext filterContext)
            {
                // 判断是否已经验证用户
                if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
                {
                    // 如果没有验证则跳转到LogOn页面
                    filterContext.HttpContext.Response.Redirect("/Account/LogOn");
                }
    
                //获得url请求里的controller和action:
                string strControllerName = filterContext.RouteData.Values["controller"].ToString().ToLower();
                string strActionName = filterContext.RouteData.Values["action"].ToString().ToLower();
    
                //根据请求过来的controller和action去查询可以被哪些角色操作:
                Models.RoleWithControllerAction roleWithControllerAction =
                    SampleData.roleWithControllerAndAction.Find(r => r.ControllerName.ToLower() == strControllerName &&
                    r.ActionName.ToLower() == strActionName);
    
                if (roleWithControllerAction != null)
                {
                    //有权限操作当前控制器和Action的角色id
                    this.Roles = roleWithControllerAction.RoleIds;    
                }
    
                base.OnAuthorization(filterContext);
            }
            /// <summary>
            /// 自定义授权检查(返回False则授权失败)
            /// </summary>
            protected override bool AuthorizeCore(HttpContextBase httpContext)
            {
    
                if (httpContext.User.Identity.IsAuthenticated)
                {
                    //当前登录用户的用户名
                    string userName = httpContext.User.Identity.Name;
                    //当前登录用户对象
                    User user = SampleData.users.Find(u => u.UserName == userName);   
    
                    if (user != null)
                    {
                        //当前登录用户的角色
                        Role role = SampleData.roles.Find(r => r.Id == user.RoleId);  
                        foreach (string roleid in Roles.Split(','))
                        {
                            if (role.Id.ToString() == roleid)
                                return true;
                        }
                        return false;
                    }
                    else
                        return false;
                }
                else
                {
                    //进入HandleUnauthorizedRequest
                    return false;      
                }
    
            }
    
            /// <summary>
            /// 处理授权失败的HTTP请求
            /// </summary>
            protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
            {
                filterContext.Result = new ViewResult { ViewName = AuthorizationFailView };
            }
        }
    }

    4、添加Account控制器

    Account控制器里面的LogOn方法用来显示登陆界面,控制器代码如下:

    using MVCCustomerFilterDemo.Models;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Security;
    
    namespace MVCCustomerFilterDemo.Controllers
    {
        public class AccountController : Controller
        {
    
            // GET: Account
            public ActionResult Index()
            {
                return View();
            }
    
            /// <summary>
            /// 显示登录视图
            /// </summary>
            /// <returns></returns>
            public ActionResult LogOn()
            {
                LogOnViewModel model = new LogOnViewModel();
                return View(model);
    
            }
    
            /// <summary>
            /// 处理用户点击登录提交回发的表单
            /// </summary>
            /// <param name="model"></param>
            /// <returns></returns>
            [HttpPost]
            public ActionResult LogOn(LogOnViewModel model)
            {
                //只要输入的用户名和密码一样就过
                if (model.UserName.Trim() == model.Password.Trim())
                {
                    // 判断是否勾选了记住我
                    if (model.RememberMe)
                    {
                        //2880分钟有效期的cookie
                        FormsAuthentication.SetAuthCookie(model.UserName, true);
                    }
                    else
                    {
                        //会话cookie
                        FormsAuthentication.SetAuthCookie(model.UserName, false);
                    }
                    // 跳转到AuthFilters控制器的Welcome方法
                    return RedirectToAction("Welcome", "AuthFilters");
                }
                else
                {
                    return View(model);
                }
    
            }
    
            /// <summary>
            /// 注销
            /// </summary>
            /// <returns></returns>
            public ActionResult LogOut()
            {
                Session.Abandon();
                FormsAuthentication.SignOut();
                return RedirectToAction("LogOn");
            }
        }
    }

    LogOn方法对应的视图页面代码如下:

    @model MVCCustomerFilterDemo.Models.LogOnViewModel
    @{
        Layout = null;
    }
    
    <!DOCTYPE html>
    
    <html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>LogOn</title>
    </head>
    <body>
        @using (Html.BeginForm())
        {
            @Html.AntiForgeryToken()
    
            <div class="form-horizontal">
                <h4>登录</h4>
                <hr />
                @Html.ValidationSummary(true)
    
                <div class="form-group">
                    @Html.LabelFor(model => model.UserName, new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.UserName)
                        @Html.ValidationMessageFor(model => model.UserName)
                    </div>
                </div>
    
                <div class="form-group">
                    @Html.LabelFor(model => model.Password, new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.Password)
                        @Html.ValidationMessageFor(model => model.Password)
                    </div>
                </div>
    
                <div class="form-group">
                    @Html.LabelFor(model => model.RememberMe, new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.RememberMe)
                        @Html.ValidationMessageFor(model => model.RememberMe)
                    </div>
                </div>
    
                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <input type="submit" value="登录" class="btn btn-default" />
                    </div>
                </div>
            </div>
        }
    </body>
    </html>

    5、修改配置文件

    修改配置文件,定义权限验证失败时跳转的页面,代码如下:

    <!--配置登录页面-->
    <authentication mode="Forms">
        <forms loginUrl="~/Account/LogOn" timeout="2880" />
    </authentication>

    6、添加授权控制器

    添加AuthFilters控制器,代码如下:

    using MVCCustomerFilterDemo.Extensions;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    
    namespace MVCCustomerFilterDemo.Controllers
    {
        public class AuthFiltersController : Controller
        {
            // GET: AuthFilters
            public ActionResult Index()
            {
                return View();
            }
    
            /// <summary>
            /// 使用自定义的授权验证,登录成功就可以访问
            /// </summary>
            /// <returns></returns>
            [Authorize]
            public ActionResult Welcome()
            {
                return View();
            }
    
    [UserAuthorize(AuthorizationFailView = "Error")]
    public ActionResult AdminUser() { ViewBag.Message = "管理员页面"; return View("Welcome"); } /// <summary> /// 会员页面(管理员、会员都可访问) /// </summary> /// <returns></returns> [Authorize] [UserAuthorize(AuthorizationFailView = "Error")] public ActionResult SeniorUser() { ViewBag.Message = "高级会员页面"; return View("Welcome"); } /// <summary> /// 游客页面(管理员、会员、游客都可访问) /// </summary> /// <returns></returns> [Authorize] [UserAuthorize(AuthorizationFailView = "Error")] public ActionResult JuniorUser() { ViewBag.Message = "初级会员页面"; return View("Welcome"); } } }

    三、测试

    1、测试Welcome

    Welcome这个Action使用了默认的授权验证,只要登录成功就能访问。

    URL地址栏里面输入:http://localhost:****/AuthFilters/Welcome,会跳转到登录页面,如图所示:

    然后输入相同的用户名和密码,点击登录,会显示Welcome对应的页面:

    在看一下SampleData中,角色为1,2的也可以访问Welcome方法,用角色1访问Welcome:

    点击登录:

    从上面的截图中看出:senior1登录成功了,senior1是角色2,证明角色1、2可以访问Welcome方法。在使用junior2登录名访问Welcome方法:

    由于junior2的角色是3,而角色3没有访问Welcome方法的权限,所以会跳转到Error页面:

    四、总结

    Welcome这个Action使用了默认的授权验证,只要登陆成功就可以访问。其他几个Action上都标注了自定义的UserAuthorize,并没有标注Users="....",Roles=".....",因为这样在Action上写死用户或者角色控制权限显然是不可行的,用户和角色的对应以及不同的角色可以操作的Action应该是从数据库里取出来的。为了演示就在SampleData类里初始化了一些用户和角色信息,根据SampleData类的定义,很明显jxl拥有1号管理员角色,可以访问AuthFilters这个控制器下的所有Action;senior1、senior2拥有2号高级会员的角色,可以访问AuthFilters这个控制器下除了AdminUser之外的Action等等。
    再次登陆下,就发现拥有高级会员角色的用户senior1是不可以访问AdminUser这个Action,会被带到AuthorizationFailView属性指定的Error视图。

    GitHub代码地址:git@github.com:JiangXiaoLiang1988/MVCCustomerFilterDemo.git

  • 相关阅读:
    hdu 1199 Color the Ball 离散线段树
    poj 2623 Sequence Median 堆的灵活运用
    hdu 2251 Dungeon Master bfs
    HDU 1166 敌兵布阵 线段树
    UVALive 4426 Blast the Enemy! 计算几何求重心
    UVALive 4425 Another Brick in the Wall 暴力
    UVALive 4423 String LD 暴力
    UVALive 4872 Underground Cables 最小生成树
    UVALive 4870 Roller Coaster 01背包
    UVALive 4869 Profits DP
  • 原文地址:https://www.cnblogs.com/dotnet261010/p/10845650.html
Copyright © 2011-2022 走看看