我明白眼前都是气泡,安静的才是苦口良药。 明白什么才让我骄傲,也明白你!——2019/1/21
过滤器(Filters)的出现使得我们可以在ASP.NET MVC程序里更好的控制浏览器请求过来的URL,不是每个请求都会响应内容,只响应特定内容给那些有特定权限的用户,过滤器理论上有以下功能:
- 判断登录与否或用户权限
- 决策输出缓存
- 防盗链 防蜘蛛
- 本地化与国际化设置
- 实现动态Action(做权限管理系统的好东西)
- 决策输出缓存 防盗链 防蜘蛛 本地化与国际化设置 实现动态Action(最适合做权限管理系统)
- 异常处理
最常用的应该是权限管理,和异常处理。会分为两篇介绍,这里先上权限管理:
先看框架内自带 的Authorize,注释一目了然,访问只限于满足授权要求的用户。这里大家肯定会有个疑问,什么才是满足授权了呢?往后面看
这里有两个视图,Index没有加授权验证,welcome加了
public ActionResult Index()
{
return View();
}
//[Authorize(Users = "ybb")]
[Authorize]
public ActionResult Welcome()
{
return View();
}
然后我们在web.config里配置一下:
<authentication mode="Forms">
<forms loginUrl="/Account/Login" timeout="30"/> //未通过授权验证则会被重定向到URL指定页面
</authentication>
关于authentication身份验证的配置信息,可以看下 https://www.microsoft.com/china/msdn/msdnmvp/page8_1.html 里的介绍
运行项目,在地址栏里直接输入/home/index,成功打开页面,没问题。
然后我们再用同样的方法访问加了授权验证的页面:
很显然,第一个名为Index的Action是没有过滤的,任何身份的请求都可以通过,只要在浏览器的URL栏里键入:localhost:****/Home/Index 就能得到对应的视图响应;
而第二个名为Welcome的Action上面标注了[Authorize],表示这是一个只处理那些通过身份验证的URL请求,如果没有通过身份验证就请求这个Action会被带到config里配置的URL页面。既然拒绝了未验证的用户,那就登录下通过验证:
我们来模拟下用户登录:
只看user,先不管后面的角色和角色控制器(后面会用到)
//假装userModel里是从数据库取出来的数据
public class UserModel
{
public static List<User> users;//用户数据
public static List<Role> roles;//权限数据
public static List<RoleWithControllerAction> roleWithControllerAndAction;//权限对应的访问权利
static UserModel()
{
// 初始化用户
users = new List<User>(){
new User(){ Id=1, UserName="ybb", RoleId=1,UserPass="123"},
new User(){ Id=1, UserName="admin", RoleId=1,UserPass="123"},
new User(){ Id=2, UserName ="senior1", RoleId=2,UserPass="123"},
new User(){ Id=3, UserName ="senior2", RoleId=2,UserPass="123"},
new User(){ Id=5, UserName="junior1", RoleId=3,UserPass="123"},
new User(){ Id=6, UserName="junior2", RoleId=3,UserPass="123"},
new User(){ Id=6, UserName="junior3", RoleId=3,UserPass="123"}
};
// 初始化角色
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="Home", ActionName="AdminUser", RoleIds="1"},
new RoleWithControllerAction(){ Id=2, ControllerName="Home", ActionName="SeniorUser",RoleIds="1,2"},
new RoleWithControllerAction(){ Id=3, ControllerName="Home", ActionName="JuniorUser",RoleIds="1,2,3"},
new RoleWithControllerAction(){ Id=4, ControllerName="Home", ActionName="Index", RoleIds="2,3"}
};
}
}
新建一个AccountController,并新建两个Action:
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult LoginResult()
{
string userName = Request.Form["userName"];
string userPass = Request.Form["userPass"];
var user = UserModel.users.Where(u => u.UserName == userName && u.UserPass == userPass);//伪代码,验证账号密码是否正确
if(user.Count()>0)
{
FormsAuthentication.SetAuthCookie(userName, false);//这一句是关键, 创建提供的用户名称的身份验证票证,并将其添加到的响应 cookie 集合或 URL
return RedirectToAction("Welcome", "Home");
}else
{
return RedirectToAction("Login", "Account");
}
}
Login
当我们输入账户密码,点击登录,这次我们通过验证成功访问了welcome页面
不知道你们有没有注意到Welcome 上面我注释掉的内容,我们甚至可以设置只有名为“ybb”或者admin的用户可以访问欢迎页面:
[Authorize(Users ="ybb,admin")]
public ActionResult Welcome()
{
return View();
}
像你所想的那样,只有使用ybb或者admin登录,才可以正常显示这个页面,使用其他账号,就又会被带到登录页。这里就不贴图了,可以自己去试试看。
先不管为何在Action上标注Users = "ybb"就可以控制可以访问的用户,但从操作性上来说这样控制Action的访问权限还是很方便的。但是如果项目大,用户对应的角色和权限变化比较大,每次变化都来重新标注Action显然不合适。MVC框架提供的过滤器(Filters)就派上了用场:
上图是Asp.Net MVC框架提供的几种默认Filter:授权筛选器、操作筛选器、结果筛选器、异常筛选器,下面来一一讲解,先看演示Demo结构图:
一、授权筛选器
授权筛选器用于实现IAuthorizationFilter接口和做出关于是否执行操作方法(如执行身份验证或验证请求的属性)的安全决策。 AuthorizeAttribute类和RequireHttpsAttribute类是授权筛选器的示例。授权筛选器在任何其他筛选器之前运行。
新建一个继承AuthorizeAttribute类的UserAuthorize类,F12定位到AuthorizeAttribute类,看看内部申明:
上面演示的指定用户才可以访问就是利用了Users属性,并由基类帮助我们验证,只放指定的Users用户通过。要实现自定义的验证只需重写下OnAuthorization和AuthorizeCore方法
/// 自定义用户授权
/// </summary>
public class UserAuthorize : AuthorizeAttribute
{
/// <summary>
/// 授权失败时呈现的视图
/// </summary>
public string AuthorizationFailView { get; set; }
/// <summary>
/// 请求授权时执行
/// </summary>
public override void OnAuthorization(AuthorizationContext filterContext)
{
//获得url请求里的controller和action:
string controllerName = filterContext.RouteData.Values["controller"].ToString().ToLower();
string actionName = filterContext.RouteData.Values["action"].ToString().ToLower();
//根据请求过来的controller和action去查询可以被哪些角色操作:
RoleWithControllerAction roleWithControllerAction = UserModel.roleWithControllerAndAction.Find(r => r.ControllerName.ToLower() == controllerName && r.ActionName.ToLower() == actionName);
if (roleWithControllerAction != null)
{
this.Roles = roleWithControllerAction.RoleIds; //有权限操作当前控制器和Action的角色id
}
base.OnAuthorization(filterContext); //进入AuthorizeCore
}
/// <summary>
/// 自定义授权检查(返回False则授权失败)
/// </summary>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.User.Identity.IsAuthenticated)
{
string userName = httpContext.User.Identity.Name; //当前登录用户的用户名
User user = UserModel.users.Find(u => u.UserName == userName); //当前登录用户对象
if (user != null)
{
Role role = UserModel.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
return false; //进入HandleUnauthorizedRequest
}
/// <summary>
/// 处理授权失败的HTTP请求
/// </summary>
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new ViewResult { ViewName = AuthorizationFailView };
}
}
自定义好授权类就可以到控制器上使用了,我们要新建好几个页面:
[Authorize]
public ActionResult Welcome()
{
ViewBag.Message = "普通已授权页面";
return View();
}
[UserAuthorize(AuthorizationFailView = "Exception")] //管理员页面
public ActionResult AdminUser()
{
ViewBag.Message = "管理员页面";
return View();
}
[UserAuthorize(AuthorizationFailView = "Exception")] //会员页面(管理员、会员都可访问)
public ActionResult SeniorUser()
{
ViewBag.Message = "高级会员页面";
return View();
}
[UserAuthorize(AuthorizationFailView = "Exception")] //游客页面(管理员、会员、游客都可访问)
public ActionResult JuniorUser()
{
ViewBag.Message = "初级会员页面";
return View();
}
public ActionResult Exception() //无权限访问跳转的页面
{
return View();
}
这个时候再使用UserModel里面不同权限的账号测试下:
没有达到对应的权限:就会跳转到通过AuthorizationFailView 设置的Exception页面
二、操作筛选器、结果筛选器
操作筛选器用于实现IActionFilter接口以及包装操作方法执行。IActionFilter接口声明两个方法:OnActionExecuting和OnActionExecuted。OnActionExecuting在操作方法之前运行。OnActionExecuted在操作方法之后运行,可以执行其他处理,如向操作方法提供额外数据、检查返回值或取消执行操作方法。
结果筛选器用于实现IResultFilter接口以及包装ActionResult对象的执行。IResultFilter接口声明两个方法OnResultExecuting和OnResultExecuted。OnResultExecuting在执行ActionResult对象之前运行。OnResultExecuted在结果之后运行,可以对结果执行其他处理,如修改 HTTP 响应。OutputCacheAttribute 类是结果筛选器的一个示例。
操作筛选器和结果筛选器都实现ActionFilterAttribute类,看看类里定义的方法:
public virtual void OnActionExecuted(ActionExecutedContext filterContext);
public virtual void OnActionExecuting(ActionExecutingContext filterContext);
public virtual void OnResultExecuted(ResultExecutedContext filterContext);
public virtual void OnResultExecuting(ResultExecutingContext filterContext);
根据方法的名字就知道4个方法执行的顺序了:
OnActionExecuting是Action执行前的操作、OnActionExecuted则是Action执行后的操作、OnResultExecuting是解析ActionResult前执行、OnResultExecuted是解析ActionResult后执行
即:
Action执行前:OnActionExecuting方法先执行→Action执行 →OnActionExecuted方法执行→OnResultExecuting方法执行→返回的ActionRsult中的 executeResult方法执行→OnResultExecuted执行
完全可以重写OnActionExecuting方法实现上面授权筛选器一样的功能,因为OnActionExecuting方法是在Action方法执行前运行的,自定义一个实现ActionFilterAttribute类的ActionFilters类。
这里的Demo我没有具体的做,就不贴代码了,不过这次是继承ActionFilterAttribute类,要注意下,其他的写法差不多。
三、异常筛选器
异常筛选器用于实现IExceptionFilter接口,并在ASP.NET MVC管道执行期间引发了未处理的异常时执行。异常筛选器可用于执行诸如日志记录或显示错误页之类的任务。自定义异常筛选器继承自HandleErrorAttribute
具体的Demo我单独写了一篇博客方便以后自己做异常处理和日志记录的时候查看和Copy代码
地址:https://blog.csdn.net/qq_41885871/article/details/86594338
源文参考自:https://www.cnblogs.com/oppoic/p/mvc_authorization_action_result_exception_filters.html