传统的登录验证方式,是通过将用户的登录状态信息保存在服务端的Session中,再利用客户端浏览器的Cookie保存SessionID,这样浏览器每次在向服务端发起请求时,都会携带该Cookie值,服务端可以根据相应的SessionID来获取匹配的Session信息,并进行验证。
在ASP.NET MVC中,可以通过使用过滤器来在请求的处理过程中,执行一些统一或额外的操作。
过滤器类型 | 接口 | 默认实现 | 说明 |
---|---|---|---|
Authorization | IAuthorizationFilter | AuthorizeAttribute | 在其他过滤器执行前运行,用于控制对指定Controller或Action的访问,必须满足授权要求。 |
Action | IActionFilter | ActionFilterAttribute | 在Action执行之前或之后运行。 |
Result | IResultFilter | ActionFilterAttribute | 在结果返回之前或之后运行。 |
Exception | IExceptionFilter | HandleErrorAttribute | 用于处理由指定Controller或Action中引发的异常。 |
AuthorizeAttribute可以限制指定的Controller或Action,只能够被满足授权要求的用户进行访问。
当使用AuthorizeAttribute标记一个Controller时,如果希望允许某些Action可以被匿名访问,则可以在该Action上标记AllowAnonymousAttribute。
[Authorize]
public class HomeController : Controller
{
/// <summary>
/// 首页
/// </summary>
/// <returns></returns>
public ActionResult Index()
{
return View();
}
/// <summary>
/// 登录页
/// </summary>
/// <returns></returns>
[AllowAnonymous]
public ActionResult Login(string account, string password)
{
if (string.IsNullOrEmpty(account))
{
return View();
}
System.Web.Security.FormsAuthentication.SetAuthCookie(account, false);
return this.RedirectToAction("Index");
}
}
一个简单的Demo如下所示:
首先,需要在Web.config中的system.web节点下添加以下内容:
<authentication mode="Forms">
<forms loginUrl="~/Home/Login" name="login"></forms>
</authentication>
<authorization> 用于配置 ASP.NET 身份验证方案,mode用于指定应用程序的默认身份验证模式,取值范围共分为 Windows、Form、Passport、None 四种验证模式。
<forms>用于配置 基于窗体的自定义身份验证,其中的loginUrl用于设置在没有找到任何有效的身份验证的Cookie时,重定向到登录页面的URL,其默认值为 login.aspx;name则设置了用于存储身份验证信息的Cookie名称,默认值为 ".ASPXAUTH";
接着,在需要进行身份验证的Action上使用AuthorizeAttribute进行标记:
/// <summary>
/// 首页
/// </summary>
/// <returns></returns>
[Authorize]
public ActionResult Index()
{
return View();
}
最后,只需在执行登录操作的Action中调用System.Web.Security.FormsAuthentication类的SetAuthCookie方法,将登录信息写入到Cookie中即可。
/// <summary>
/// 登录页
/// </summary>
/// <returns></returns>
public ActionResult Login(string account, string password)
{
if (string.IsNullOrEmpty(account))
{
return View();
}
System.Web.Security.FormsAuthentication.SetAuthCookie(account, false);
return this.RedirectToAction("Index");
}
当直接访问Index页面时,因为没有登录信息,所以页面将会被重定向到Login页。而在完成登录操作之后,身份验证信息将被写入到Cookie中,此时可以通过浏览器的开发者工具,查看到Cookie信息,其名称“login”正是上面在forms标签中所设置的名称。
在此之后就可以正常访问Index页面了。
此外,通过forms中的timeout可以用来手动设置存储身份验证信息的Cookie的过期时间。timeout以整数分钟为单位,其默认值为30。当slidingExpiration的值为True时,表示在距离最后一次访问的N分钟后过期,相当于在单个会话期间内,每次请求都将刷新过期时间。而False则相当于过期时间是一个绝对值,只要大于该间隔范围就会立即过期。slidingExpiration的默认值为 True。
在forms中还包含了许多其他的特性,具体可以参考MSDN。
在退出登录时,可以使用 System.Web.Security.FormsAuthentication.SignOut() 来从浏览器删除 Forms 身份验证的票证信息,实现注销功能。
/// <summary>
/// 注销
/// </summary>
/// <returns></returns>
[Authorize]
public ActionResult Logout()
{
System.Web.Security.FormsAuthentication.SignOut();
return this.RedirectToAction("Login");
}
除了使用默认实现的AuthorizeAttribute类之外,还可以通过继承AuthorizeAttribute类,实现自定义的权限验证:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AuthenticationAttribute : AuthorizeAttribute
{
// 执行顺序:OnAuthorization-->AuthorizeCore-->HandleUnauthorizedRequest
/// <summary>
/// 请求授权处理
/// </summary>
/// <param name="filterContext"></param>
public override void OnAuthorization(AuthorizationContext filterContext)
{
// 如果当前Action或Controller标记了AllowAnonymousAttribute,则跳过结束验证
if (filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true) || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
{
return;
}
// 此外,通过 filterContext.Controller 还可以获得 ViewData 或 ViewBag,可以用来存储一些临时的数据内容:
// filterContext.Controller.ViewData
// filterContext.Controller.ViewBag
// 实际上,基类所实现的 OnAuthorization 中,已经包含了对 AllowAnonymousAttribute 的验证
// 这也是为什么直接标记 AuthorizeAttribute ,在遇到 AllowAnonymousAttribute 会跳出验证的原因
// 并且,在基类的 OnAuthorization 中,还负责调用了 OnAuthorization 方法
// 如果 OnAuthorization 的返回值为 false,则将调用 HandleUnauthorizedRequest 方法
base.OnAuthorization(filterContext);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//return base.AuthorizeCore(httpContext);
// 拿到存储有身份信息的cookie对象
var cookies = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
// 如果没有拿到cookie信息,则说明当前用户尚未登录
if (cookies == null)
{
return false;
}
// 解码认证信息
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookies.Value);
// 拿到解密后的票据信息
string userData = ticket.UserData;
// ......
return true;
}
// 当AuthorizeCore返回false时,将会调用 HandleUnauthorizedRequest
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//base.HandleUnauthorizedRequest(filterContext);
filterContext.Result = new RedirectResult("~/Default/Login");
}
}
如果希望对所有的请求都应用授权过滤的话(比如统一的登录状态验证),不必为每一个Controller都单独手动的标记特性,而是可以在Global文件的Application_Start中,添加全局筛选:
GlobalFilters.Filters.Add(new AuthenticationAttribute());
而对于一些不希望进行授权验证的特殊的Controller或Action,只需标记为它们AllowAnonymousAttribute即可。
在下入门不久,还是嘎嘎新的一个幼小菜鸟,记录一下平时的学习心得,如有错误,还请诸位大神海涵轻拍,必及时更正!
参考资料