ASP.NET MVC3 Custom FormAuthorize
我们开发web系统,用户身份验证是最常见不过的。最简单的办法就是定一个基类,基类里面有判断Cookie或Session是否存在,然后决定是否跳转。今天就利用MVC的特性来一个不一样的验证方式。
- public class CustomAuthorizeAttribute : AuthorizeAttribute
- {
- protected override bool AuthorizeCore(HttpContextBase httpContext)
- {
- var user = WebUtility.GetIdentity(httpContext);
- if (user != null)
- {
- httpContext.User = new UserPrincipal(WebUtility.GetIdentity(httpContext));
- }
- return user != null;
- }
- }
这是自定义的验证机制,重写了系统自带的验证核心逻辑,下面是系统自带的逻辑:
- protected virtual bool AuthorizeCore(HttpContextBase httpContext)
- {
- if (httpContext == null)
- {
- throw new ArgumentNullException("httpContext");
- }
- IPrincipal user = httpContext.User;
- return user.Identity.IsAuthenticated && (this._usersSplit.Length <= 0 || this._usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)) && (this._rolesSplit.Length <= 0 || this._rolesSplit.Any(new Func<string, bool>(user.IsInRole)));
- }
可以看出,验证都是用httpContext.User属性来判断,所以我们对登录、退出和获取用户信息都通过httpContext.User属性就行了。从MVC的示例中我们也可以发现确实如此,看看MVC3的Views里的_LogOnPartial.cshtml的代码:
- @if(Request.IsAuthenticated) {
- <text>Welcome <strong>@User.Identity.Name</strong>!
- [ @Html.ActionLink("Log Off", "LogOff", "Account") ]</text>
- }
- else {
- @:[ @Html.ActionLink("Log On", "LogOn", "Account") ]
- }
那么现在再看我们刚才重写的验证逻辑,就大致明白了。首先通过一个外部的逻辑获取User,如果没有User说明没有登录,否则就给User赋值,标记登录了。
再看我们是如何获取这个User的
- public static User GetIdentity(HttpContextBase httpContext)
- {
- var user = httpContext.Request.Cookies[COOKIE_NAME_KEY];
- var info = httpContext.Request.Cookies[COOKIE_INFO_KEY];
- if (user == null || info == null || !info.Value.DESDecrypt(DES_KEY).Contains(user.Value))
- {
- return null;
- }
- return UserManager.GetUser(user.Value);
- }
这就很易懂了,也是通过的cookie对用户资料做的存储。
再来看第二句里的UserPrincipal类的定义:
- public class UserPrincipal : IPrincipal
- {
- private readonly User _user;
- public UserPrincipal(User user)
- {
- _user = user;
- }
- public bool IsInRole(string role)
- {
- return _user != null && _user.Role.ToString() == role;
- }
- public IIdentity Identity
- {
- get { return new UserIdentity(_user); }
- }
- }
httpContext.User是IPrincipal类型,所以我们要自定义一个UserPrincipal类,在实现时还需要实现一个IIdentity接口。
- public class UserIdentity : IIdentity
- {
- public UserIdentity(User user)
- {
- User = user;
- }
- public User User { get; private set; }
- public string Name
- {
- get { return User == null ? string.Empty : User.UserName; }
- }
- public string AuthenticationType
- {
- get { return "csdn.maddemon"; }
- }
- public bool IsAuthenticated
- {
- get { return User != null; }
- }
- }
好了,整个验证核心需要的东西我们都准备好了,还差一个最重要的东西就是我们登录的时候需要写cookie,退出的时候需要清除cookie。
- public static void SetAuthorizeCookie(HttpContextBase httpContext, User user)
- {
- httpContext.Response.SetCookie(new HttpCookie(COOKIE_NAME_KEY, user.UserName));
- httpContext.Response.SetCookie(new HttpCookie(COOKIE_INFO_KEY, (user.UserName + "|" + user.Role).DESEncrypt(DES_KEY)));
- }
- public static void RemoveAuthorizeCookie(HttpContextBase httpContext)
- {
- httpContext.Response.SetCookie(new HttpCookie(COOKIE_NAME_KEY, null) { Expires = DateTime.Now.AddDays(-1) });
- httpContext.Response.SetCookie(new HttpCookie(COOKIE_INFO_KEY, null) { Expires = DateTime.Now.AddDays(-1) });
- }
这样一套下来,需要登录的Controller或Action,我们只需要加上一个[CustomAuthorize]属性就可以,如果没有登录就会自动跳转到config里配置的登录页。是不是很不方便啊? 方便吗?不方便吗? 方便吗?不方便吗? 方便吗?不方便吗?是不是啊 哈哈
- <authentication mode="Forms">
- <forms loginUrl="~/Account/LogOn" timeout="2880" />
- </authentication>