ASP.NET MVC Forms検証メカニズム

10345 ワード

ASP.NET MVC 3
Forms認証の使用
認証プロセス
一、ユーザーログイン
1、検証フォーム:ModelState.IsValid
2、ユーザー名とパスワードの検証:データベースを検索して検証する
3、ユーザー名とパスワードが正しい場合、クライアントでCookieを保存してユーザーログイン状態を保存する:SetAuthCookie
1):データベースからユーザ名と必要な情報を検出し、追加情報をUserDataに保存する
2):ユーザー名とUserDataをFormsAuthenticationTicket手形に保存する
3):手形を暗号化するEncrypt
4):暗号化されたチケットをCookieに保存してクライアントに送信
4、ログイン前のページにジャンプ
二、ログインの検証
1.GlobalにPostAuthenticateRequestイベント関数を登録し、クライアントから送られてきたCookieデータを解析する
1):HttpContext.Current.User.Identityユーザーがログインしているかどうかを判断する(FormsIdentity,IsAuthenticated,AuthenticationType)
2):HttpContextのRequestのCookieからValueを解析し,復号してFormsAuthenticationTicketからUserDataを得る
2、ロール検証
ActionにAuthorizeプロパティを追加すると、ロール検証が可能になります.
HttpContext.Current.UserのIsInRoleメソッドによるロール認証(書き換えが必要)
 
 
次はコードです.以上使用したすべての検証クラスが再ロードされます.
一、まずユーザユーザ認証のIPrincipal
ここでは汎用属性を抽象化し,2つのIPrincipalを定義する
//       
    public class MyFormsPrincipal<TUserData> : IPrincipal
        where TUserData : class, new()
    {
        //      
        public IIdentity Identity { get; private set; }
        //    
        public TUserData UserData { get; private set; }


        public MyFormsPrincipal(FormsAuthenticationTicket ticket, TUserData userData)
        {
            if (ticket == null)
                throw new ArgumentNullException("ticket");
            if (userData == null)
                throw new ArgumentNullException("userData");

            Identity = new FormsIdentity(ticket);
            UserData = userData;
        }

        //    
        public bool IsInRole(string role)
        {
            var userData = UserData as MyUserDataPrincipal;
            if (userData == null)
                throw new NotImplementedException();

            return userData.IsInRole(role);
        }

        //     
        public bool IsInUser(string user)
        {
            var userData = UserData as MyUserDataPrincipal;
            if (userData == null)
                throw new NotImplementedException();

            return userData.IsInUser(user);
        }
    }
汎用エンティティには、データエンティティを格納し、ロール検証とユーザー検証を特定のデータエンティティに格納できます.
//         
    public class MyUserDataPrincipal : IPrincipal
    {
        //   
        private readonly MingshiEntities mingshiDb = new MingshiEntities();

        public int UserId { get; set; }
        
        //            
        public List<int> RoleId { get; set; }

        //   Authorize   ,           
        public bool IsInRole(string role)
        {
            //          
            var userroles = mingshiDb.UserRole.Where(u => u.UserId == UserId).Select(u => u.Role.RoleName).ToList();

            var roles = role.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
            return (from s in roles from userrole in userroles where s.Equals(userrole) select s).Any();
        }

        //      
        public bool IsInUser(string user)
        {
            //          
            var users = user.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            return mingshiDb.User.Any(u => users.Contains(u.UserName));
        }

        
        [ScriptIgnore]    //            
        public IIdentity Identity { get { throw new NotImplementedException(); } }
    }
二、Cookieの検証と設定のためのFormsAuthentication
 
//     
    public class MyFormsAuthentication<TUserData> where TUserData : class, new()
    {
        //Cookie     
        private const int CookieSaveDays = 14;

        //         Cookie
        public static void SetAuthCookie(string username, TUserData userData, bool rememberMe)
        {
            if (userData == null)
                throw new ArgumentNullException("userData");

            var data = (new JavaScriptSerializer()).Serialize(userData);

            //  ticket
            var ticket = new FormsAuthenticationTicket(
                2, username, DateTime.Now, DateTime.Now.AddDays(CookieSaveDays), rememberMe, data);

            //  ticket
            var cookieValue = FormsAuthentication.Encrypt(ticket);

            //  Cookie
            var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue)
            {
                HttpOnly = true,
                Secure = FormsAuthentication.RequireSSL,
                Domain = FormsAuthentication.CookieDomain,
                Path = FormsAuthentication.FormsCookiePath,
            };
            if (rememberMe)
                cookie.Expires = DateTime.Now.AddDays(CookieSaveDays);

            //  Cookie
            HttpContext.Current.Response.Cookies.Remove(cookie.Name);
            HttpContext.Current.Response.Cookies.Add(cookie);
        }

        // Request    Ticket,UserData
        public static MyFormsPrincipal<TUserData> TryParsePrincipal(HttpRequest request)
        {
            if (request == null)
                throw new ArgumentNullException("request");

            // 1.    Cookie
            var cookie = request.Cookies[FormsAuthentication.FormsCookieName];
            if (cookie == null || string.IsNullOrEmpty(cookie.Value)) return null;

            try
            {
                // 2.   Cookie ,  FormsAuthenticationTicket  
                var ticket = FormsAuthentication.Decrypt(cookie.Value);
                if (ticket != null && !string.IsNullOrEmpty(ticket.UserData))
                {
                    var userData = (new JavaScriptSerializer()).Deserialize<TUserData>(ticket.UserData);
                    if (userData != null)
                    {
                        return new MyFormsPrincipal<TUserData>(ticket, userData);
                    }
                }
                return null;
            }
            catch
            {
                /*         ,       。 */
                return null;
            }
        }
    }
三、ロールとユーザー名を検証するためのAuthorize特性
//          
    public class MyAuthorizeAttribute : AuthorizeAttribute
    {
        protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
        {
            var user = httpContext.User as MyFormsPrincipal<MyUserDataPrincipal>;
            if (user != null)
                return (user.IsInRole(Roles) || user.IsInUser(Users));

            return false;
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            //     ,         ,  :         ,      Action  
            filterContext.Result = new RedirectResult("http://www.baidu.com");
        }
    }
 
では、4つのクラス定義が完了しました.次は
1、まずは上陸
[HttpPost]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
          //         
                var bll= new UserBll();
                var userId = bll.Validate(model.UserName, model.Password, HttpContext.Request.UserHostAddress, HttpContext.Request.UserAgent);
                if (userId > 0)
                {
                    //    ,       ,      (        ,       Id)
                    var userData = new MyUserDataPrincipal {UserId = userId};

                    //  Cookie
                    MyFormsAuthentication<MyUserDataPrincipal>.SetAuthCookie(model.UserName, userData, model.RememberMe);

                    if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                        && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Index", "Home");
                    }
                }
                else
                {
                    ModelState.AddModelError("", "            。");
                }
            }

            //                  ,       
            return View(model);
        }
 
二、ログイン完了後、検証であり、検証前にクライアントのユーザデータを取得する(前に設定したCookieから解析)
グローバルファイル:Global.asaxに次のコードを追加
protected void Application_PostAuthenticateRequest(object sender, System.EventArgs e)
        {
            var formsIdentity = HttpContext.Current.User.Identity as FormsIdentity;
            if (formsIdentity != null && formsIdentity.IsAuthenticated && formsIdentity.AuthenticationType == "Forms")
            {
                HttpContext.Current.User =
                    MyFormsAuthentication<MyUserDataPrincipal>.TryParsePrincipal(HttpContext.Current.Request);
            }
        }
 
これによりRequestからUserDataが解析され,以下で検証に用いることができる.
三、ロール検証が必要なアクションに[MyAuthorize]特性を追加する
[MyAuthorize(Roles = "User", Users = "bomo,toroto")]
        public ActionResult About()
        {
            return View();
        }
ユーザがこのアクションにアクセスすると、MyAuthorizeのAuthorizeCoreメソッドを呼び出して検証し、検証に成功した場合は実行を続行し、検証に失敗した場合はHandleUnauthorizedRequestメソッドを呼び出して対応する処理を行い、ここで定義したRolesとUsersをMyAuthorizeで取得して検証する