ASP.NET MVC 5.0已经发布一段时间了,适应了一段时间,准备把原来的MVC项目重构了一遍,先把基本权限验证这块记录一下。
环境:Windows 7 Professional SP1 + Microsoft Visual Studio 2013(MVC 5 + Web API 2)
修改Web.config,增加Forms验证模式,在system.web节点中增加以下配置:
<authentication mode="Forms"> <forms loginUrl="~/login" defaultUrl="~/" protection="All" timeout="20" name="__auth" /> </authentication>
【MVC View Controller 篇】
新建一个PageAuth,继承自AuthorizeAttribute:
using System; using System.Net; using System.Web; using System.Web.Mvc; using System.Web.Security;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class PageAuth : AuthorizeAttribute { protected override bool AuthorizeCore(HttpContextBase httpContext) { if (httpContext == null) { return false; } if (httpContext.User.Identity.IsAuthenticated && base.AuthorizeCore(httpContext)) { return ValidateUser(); } httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; return false; } public override void OnAuthorization(AuthorizationContext filterContext) { base.OnAuthorization(filterContext); if (filterContext.HttpContext.Response.StatusCode == (int)HttpStatusCode.Forbidden) { filterContext.Result = new RedirectToRouteResult("AccessErrorPage", null); } } protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl); } private bool ValidateUser() { //TODO: 权限验证 return true; } }
建一个Controller的基类PageBase,继承自Controller:
using System.Web.Mvc;
[PageAuth] public class PageBase : Controller { }
所有View的Controller均继承自PageBase,不再继承自Controller。
继承PageBase之后,所有的Controller均需登录,给允许匿名访问的Controller(或Action)增加AllowAnonymous(以AccountController为例):
using System.Web.Mvc;
public class AccountController : PageBase { [AllowAnonymous] public ActionResult Login() // 可匿名访问 { ViewBag.Title = "用户登录"; return View(); } public ActionResult Detail(int id) // 需登录访问 { ViewBag.Title = "用户详情"; return View(); } }
页面Controller的开发,基本结束,接下来就是在登录页面(~/login)使用js提交登录信息,用post方式提交。
提交之后,需要开发Web API的接口了。
【MVC Web API Controller 篇】
同样,新建一个ApiAuth,继承自ActionFilterAttribute:
using System; using System.Net; using System.Net.Http; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Filters; using System.Web.Security;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class ApiAuth : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { try { if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0) // 允许匿名访问 { base.OnActionExecuting(actionContext); return; } var cookie = actionContext.Request.Headers.GetCookies(); if (cookie == null || cookie.Count < 1) { actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden); return; } FormsAuthenticationTicket ticket = null; foreach (var perCookie in cookie[0].Cookies) { if (perCookie.Name == FormsAuthentication.FormsCookieName) { ticket = FormsAuthentication.Decrypt(perCookie.Value); break; } } if (ticket == null) { actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden); return; } // TODO: 添加其它验证方法 base.OnActionExecuting(actionContext); } catch { actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden); } } }
新建一个ApiController的基类ApiBase,继承自ApiController:
using System.Web.Http;
[ApiAuth] public class ApiBase : ApiController { }
所有API的Controller均继承自ApiBase,不再继承自ApiController。
继承ApiBase之后,给允许匿名访问的Controller(或Action)增加AllowAnonymous(以LoginController为例):
using System.Web.Http; using System.Web.Security;
public class LoginController : ApiBase { [HttpPost] [AllowAnonymous] public bool Login([FromBody]LoginInfo loginInfo) { try { var cookie = FormsAuthentication.GetAuthCookie("Username", false); var ticket = FormsAuthentication.Decrypt(cookie.Value); var newTicket = new FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, ""); cookie.Value = FormsAuthentication.Encrypt(newTicket); DeyiContext.Response.Cookies.Add(cookie);return true; } catch { return false; } } }
【写在最后】
网上查了很多方法,还需要时间验证一下各个方法的合理度。
关于Web API的安全性,个人觉得,还是采用SSL的方式更加稳妥一些。
另外,网上很多写的在Web API的权限判断的时候,使用的是actionContext.Request.Headers.Authorization来判断,如下:
if (actionContext.Request.Headers.Authorization == null) { // 判断是否允许匿名访问 } else { var ticket = FormsAuthentication.Decrypt(actionContext.Request.Headers.Authorization.Parameter); // 后续其它验证操作 }
还没有完成测试该方法,慢慢来吧~~~