[ASP.NET MVC]使用IAuthenticationFilter,IAuthorizationFilter实践Form表单登入认证&授权

实际上大多情境并不采用ASP.NET Identity处理身份认证授权,而是选择使用Form表单认证轻量且易于快速实现自订逻辑,网络上也有许多教学如何在ASP.NET中使用FormIdentity。
此篇将提供的是在ASP.NET MVC 5 使用IAuthenticationFilter,IAuthorizationFilter实现Form表单认证的方法
启用Form表单认证
修改Web.config将authentication mode改为Forms表单认证(默认是None)
注解remove,启用FormsAuthentication模块(ASP.NET 5 项目范本默认移除的)
使用FormIdentity实践登入逻辑
通过自订逻辑后,将使用者资讯放入FormsAuthenticationTicket
的userData
参数,经由加密并建立cookie加入Response回传给Client
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid) {
return View();
}
var user = _db.Users
.FirstOrDefault(x => x.Email == model.Email);
if (user == null) {
ModelState.AddModelError("", "请输入正确的账号或密码!");
return View();
}
if (user.Password.Equals(model.Password)) {
//string roles = string.Join(",", user.Roles.Select(x => x.Name).ToArray());
var now = DateTime.Now;
var ticket = new FormsAuthenticationTicket(
version: 1,
name: user.Email,
issueDate: now,
expiration: now.AddMinutes(30),
isPersistent: model.RememberMe,
userData: user.Id.ToString(),//userData:roles,
cookiePath: FormsAuthentication.FormsCookiePath);
var encryptedTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
Response.Cookies.Add(cookie);
return RedirectToAction("Index", "Home");
}
else {
ModelState.AddModelError("", "请输入正确的账号或密码!");
return View();
}
}
IAuthenticationFilter认证过滤器-提供自定义登入认证逻辑
Authentication filter是ASP.NET MVC 5的新过滤器,可以在Action方法或Controller或全域设置至全部Controller的验证逻辑。
在ASP.NET MVC 5处理管线中会先执行认证过滤器OnAuthentication
(提供AuthenticationContext
包含认证主体IPrincipal
),进行认证检核。
另外提供OnAuthenticationChallenge
将未经授权的请求进行额外处里(如响应自订ChallengeResult)再响应。
Lifecycle of an ASP.NET MVC 5 Application
public class CustomAuthenticationFilter : IAuthenticationFilter
{
private readonly Database _db = new Database();
public void OnAuthentication(AuthenticationContext filterContext)
{
if (filterContext.ActionDescriptor.IsDefined(typeof (AllowAnonymousAttribute), inherit: true)
|| filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof (AllowAnonymousAttribute), inherit: true)) {
return;
}
if (filterContext.Principal.Identity.IsAuthenticated && filterContext.Principal.Identity is FormsIdentity)
{
var identity = (FormsIdentity)filterContext.Principal.Identity;
var ticket = identity.Ticket;
if (!string.IsNullOrEmpty(ticket.UserData))
{
//var roles = ticket.UserData.Split(',');
//filterContext.Principal = new GenericPrincipal(identity,roles);
var user = _db.Users.FirstOrDefault(u => u.Id.ToString() == ticket.UserData);
if (user != null) {
var roles = user.Roles.Select(r => r.Name).ToArray();
filterContext.Principal = new GenericPrincipal(identity, roles);
}
}
}
else
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
if (filterContext.Result == null || filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary
{
{"controller","Account"},
{"action","Login"},
{"returnUrl",filterContext.HttpContext.Request.RawUrl }
});
}
//or do something , add challenge to response
}
}
IAuthorizationFilter授权过滤器-提供自定义身份(角色)授权逻辑
Authorization filter验证使用者是否拥有权限或角色身份执行Action,ASP.NET MVC 已内建提供实践AuthorizeAttribute
只需继承并覆写 AuthorizeCore()
方法提供自订逻辑。
直接继承IAuthorizationFilter界面实践需要考量更多网络安全性的问题,为了避免发生漏洞或是特性不完全,所以Framwork已经提供经由广泛测试且功能完善的良好实践类,只需复用即可。白话:避免明明是自己写不好,却要怪是微软的漏洞xDD
Github:AuthorizeAttribute.cs 有兴趣或有需要自制可以前往察看源代码
public class CustomAuthorizationFilter : AuthorizeAttribute
{
private readonly Database _db = new Database();
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.Request.IsLocal) {
return true;
}
var identity = httpContext.User.Identity as FormsIdentity;
if (identity ?.Ticket != null)
{
//var userRoles = identity.Ticket.UserData.Split(',');
var userRoles = _db.Users.Find(identity.Ticket.UserData) ?.Roles.Select(r => r.Name);
if (userRoles ?.Intersect(Roles.Split(',')).Any() ?? false)//交集->具有某一角色->有权限
{
return true;
}
//or get current action's roles from db and check the authorization
}
return false;
}
}
补充运行流程图:
转载自:一张图看懂ASP.NET MVC5认证和授权过滤器的执行顺序
FilterConfig.cs全域注册过滤器
注册后即可达到全站套用过滤器,搭配[AllowAnonymous]
属性控制各别Action功能是否需要检查授权。
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new CustomAuthenticationFilter());
//filters.Add(new CustomAuthorizationFilter());
}
}
额外补充:
登出
[HttpPost] [ValidateAntiForgeryToken] public ActionResult LogOff() { FormsAuthentication.SignOut(); return RedirectToAction("Index", "Home"); }
3A(AAA) - Authentication、Authorization、Accounting
参考&延伸阅读
mrkt 的程序学习笔记: ASP.NET MVC 实做具有多个角色权限的登入功能
如何在ASP.NET MVC 加上简易表单验证| Yowko's Notes
KingKong Bruce记事: ASP.NET 2.0 表单身份认证心得笔记(详述Form认证运行原理)