亚洲免费乱码视频,日韩 欧美 国产 动漫 一区,97在线观看免费视频播国产,中文字幕亚洲图片

      1. <legend id="ppnor"></legend>

      2. 
        
        <sup id="ppnor"><input id="ppnor"></input></sup>
        <s id="ppnor"></s>

        詳解ASP.NET MVC Form表單驗(yàn)證

        字號(hào):


            這篇文章主要為大家詳細(xì)介紹了ASP.NET MVC Form表單驗(yàn)證,一般驗(yàn)證方式有Windows驗(yàn)證和表單驗(yàn)證,web項(xiàng)目用得更多的是表單驗(yàn)證,感興趣的小伙伴們可以參考一下
            一、前言
            關(guān)于表單驗(yàn)證,已經(jīng)有不少的文章,相信Web開(kāi)發(fā)人員也都基本寫(xiě)過(guò),最近在一個(gè)個(gè)人項(xiàng)目中剛好用到,在這里與大家分享一下。本來(lái)想從用戶(hù)注冊(cè)開(kāi)始寫(xiě)起,但發(fā)現(xiàn)東西比較多,涉及到界面、前端驗(yàn)證、前端加密、后臺(tái)解密、用戶(hù)密碼Hash、權(quán)限驗(yàn)證等等,文章寫(xiě)起來(lái)可能會(huì)很長(zhǎng),所以這里主要介紹的是登錄驗(yàn)證和權(quán)限控制部分,有興趣的朋友歡迎一起交流。
            一般驗(yàn)證方式有Windows驗(yàn)證和表單驗(yàn)證,web項(xiàng)目用得更多的是表單驗(yàn)證。原理很簡(jiǎn)單,簡(jiǎn)單地說(shuō)就是利用瀏覽器的cookie,將驗(yàn)證令牌存儲(chǔ)在客戶(hù)端瀏覽器上,cookie每次會(huì)隨請(qǐng)求發(fā)送到服務(wù)器,服務(wù)器驗(yàn)證這個(gè)令牌。通常一個(gè)系統(tǒng)的用戶(hù)會(huì)分為多種角色:匿名用戶(hù)、普通用戶(hù)和管理員;這里面又可以再細(xì)分,例如用戶(hù)可以是普通用戶(hù)或Vip用戶(hù),管理員可以是普通管理員或超級(jí)管理員等。在項(xiàng)目中,我們有的頁(yè)面可能只允許管理員查看,有的只允許登錄用戶(hù)查看,這就是角色區(qū)分(Roles);某些特別情況下,有些頁(yè)面可能只允許叫“張三”名字的人查看,這就是用戶(hù)區(qū)分(Users)。
            我們先看一下最后要實(shí)現(xiàn)的效果:
            1.這是在Action級(jí)別的控制。
            public class Home1Controller : Controller
            {
              //匿名訪(fǎng)問(wèn)
              public ActionResult Index()
              {
                return View();
              }
              //登錄用戶(hù)訪(fǎng)問(wèn)
              [RequestAuthorize]
              public ActionResult Index2()
              {
                return View();
              }
              //登錄用戶(hù),張三才能訪(fǎng)問(wèn)
              [RequestAuthorize(Users="張三")]
              public ActionResult Index3()
              {
                return View();
              }
              //管理員訪(fǎng)問(wèn)
              [RequestAuthorize(Roles="Admin")]
              public ActionResult Index4()
              {
                return View();
              }
            }
            2.這是在Controller級(jí)別的控制。當(dāng)然,如果某個(gè)Action需要匿名訪(fǎng)問(wèn),也是允許的,因?yàn)榭刂萍?jí)別上,Action優(yōu)先級(jí)大于Controller。
            //Controller級(jí)別的權(quán)限控制
            [RequestAuthorize(User="張三")]
            public class Home2Controller : Controller
            {
              //登錄用戶(hù)訪(fǎng)問(wèn)
              public ActionResult Index()
              {
                return View();
              }
              //允許匿名訪(fǎng)問(wèn)
              [AllowAnonymous]
              public ActionResult Index2()
              {
                return View();
              }
            }
            3.Area級(jí)別的控制。有時(shí)候我們會(huì)把一些模塊做成分區(qū),當(dāng)然這里也可以在Area的Controller和Action進(jìn)行標(biāo)記。
            從上面可以看到,我們需要在各個(gè)地方進(jìn)行標(biāo)記權(quán)限,如果把Roles和Users硬寫(xiě)在程序中,不是很好的做法。我希望能更簡(jiǎn)單一點(diǎn),在配置文件進(jìn)行說(shuō)明。例如如下配置:
            <?xml version="1.0" encoding="utf-8" ?>
            <!--
              1.這里可以把權(quán)限控制轉(zhuǎn)移到配置文件,這樣就不用在程序中寫(xiě)roles和users了
              2.如果程序也寫(xiě)了,那么將覆蓋配置文件的。
              3.action級(jí)別的優(yōu)先級(jí) > controller級(jí)別 > Area級(jí)別  
            -->
            <root>
             <!--area級(jí)別-->
             <area name="Admin">
              <roles>Admin</roles>
             </area>
             <!--controller級(jí)別-->
             <controller name="Home2">
              <user>張三</user>
             </controller>
             <!--action級(jí)別-->
             <controller name="Home1">
              <action name="Inde3">
               <users>張三</users>
              </action>
              <action name="Index4">
               <roles>Admin</roles>
              </action>
             </controller>
            </root>
            寫(xiě)在配置文件里,是為了方便管理,如果程序里也寫(xiě)了,將覆蓋配置文件的。ok,下面進(jìn)入正題。
            二、主要接口
            先看兩個(gè)主要用到的接口。
            IPrincipal 定義了用戶(hù)對(duì)象的基本功能,接口定義如下:
            public interface IPrincipal
            {
              //標(biāo)識(shí)對(duì)象
              IIdentity Identity { get; }
              //判斷當(dāng)前角色是否屬于指定的角色
              bool IsInRole(string role);
            }
            它有兩個(gè)主要成員,IsInRole用于判斷當(dāng)前對(duì)象是否屬于指定角色的,IIdentity定義了標(biāo)識(shí)對(duì)象信息。HttpContext的User屬性就是IPrincipal類(lèi)型的。
            IIdentity 定義了標(biāo)識(shí)對(duì)象的基本功能,接口定義如下:
            public interface IIdentity
            {  
              //身份驗(yàn)證類(lèi)型
              string AuthenticationType { get; }
              //是否驗(yàn)證通過(guò)
              bool IsAuthenticated { get; } 
              //用戶(hù)名
              string Name { get; }
            }
            IIdentity包含了一些用戶(hù)信息,但有時(shí)候我們需要存儲(chǔ)更多信息,例如用戶(hù)ID、用戶(hù)角色等,這些信息會(huì)被序列到cookie中加密保存,驗(yàn)證通過(guò)時(shí)可以解碼再反序列化獲得,狀態(tài)得以保存。例如定義一個(gè)UserData。
            public class UserData : IUserData
            {
              public long UserID { get; set; }
              public string UserName { get; set; }
              public string UserRole { get; set; }
              public bool IsInRole(string role)
              {
                if (string.IsNullOrEmpty(role))
                {
                  return true;
                }
                return role.Split(',').Any(item => item.Equals(this.UserRole, StringComparison.OrdinalIgnoreCase));      
              }
              public bool IsInUser(string user)
              {
                if (string.IsNullOrEmpty(user))
                {
                  return true;
                }
                return user.Split(',').Any(item => item.Equals(this.UserName, StringComparison.OrdinalIgnoreCase));
              }
            }
            UserData實(shí)現(xiàn)了IUserData接口,該接口定義了兩個(gè)方法:IsInRole和IsInUser,分別用于判斷當(dāng)前用戶(hù)角色和用戶(hù)名是否符合要求。該接口定義如下:
            public interface IUserData
            {
              bool IsInRole(string role);
              bool IsInUser(string user);
            }
            接下來(lái)定義一個(gè)Principal實(shí)現(xiàn)IPrincipal接口,如下:
            public class Principal : IPrincipal    
            {
              public IIdentity Identity{get;private set;}
              public IUserData UserData{get;set;}
              public Principal(FormsAuthenticationTicket ticket, IUserData userData)
              {
                EnsureHelper.EnsureNotNull(ticket, "ticket");
                EnsureHelper.EnsureNotNull(userData, "userData");
                this.Identity = new FormsIdentity(ticket);
                this.UserData = userData;
              }
              public bool IsInRole(string role)
              {
                return this.UserData.IsInRole(role);      
              }   
              public bool IsInUser(string user)
              {
                return this.UserData.IsInUser(user);
              }
            }
            Principal包含IUserData,而不是具體的UserData,這樣很容易更換一個(gè)UserData而不影響其它代碼。Principal的IsInRole和IsInUser間接調(diào)用了IUserData的同名方法。
            三、寫(xiě)入cookie和讀取cookie
            接下來(lái),需要做的就是用戶(hù)登錄成功后,創(chuàng)建UserData,序列化,再利用FormsAuthentication加密,寫(xiě)到cookie中;而請(qǐng)求到來(lái)時(shí),需要嘗試將cookie解密并反序列化。如下:
            public class HttpFormsAuthentication
            {    
              public static void SetAuthenticationCookie(string userName, IUserData userData, double rememberDays = 0)            
              {
                EnsureHelper.EnsureNotNullOrEmpty(userName, "userName");
                EnsureHelper.EnsureNotNull(userData, "userData");
                EnsureHelper.EnsureRange(rememberDays, "rememberDays", 0);
                //保存在cookie中的信息
                string userJson = JsonConvert.SerializeObject(userData);
                //創(chuàng)建用戶(hù)票據(jù)
                double tickekDays = rememberDays == 0 ? 7 : rememberDays;
                var ticket = new FormsAuthenticationTicket(2, userName,
                  DateTime.Now, DateTime.Now.AddDays(tickekDays), false, userJson);
                //FormsAuthentication提供web forms身份驗(yàn)證服務(wù)
                //加密
                string encryptValue = FormsAuthentication.Encrypt(ticket);
                //創(chuàng)建cookie
                HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptValue);
                cookie.HttpOnly = true;
                cookie.Domain = FormsAuthentication.CookieDomain;
                if (rememberDays > 0)
                {
                  cookie.Expires = DateTime.Now.AddDays(rememberDays);
                }      
                HttpContext.Current.Response.Cookies.Remove(cookie.Name);
                HttpContext.Current.Response.Cookies.Add(cookie);
              }
              public static Principal TryParsePrincipal<TUserData>(HttpContext context)              
                where TUserData : IUserData
              {
                EnsureHelper.EnsureNotNull(context, "context");
                HttpRequest request = context.Request;
                HttpCookie cookie = request.Cookies[FormsAuthentication.FormsCookieName];
                if(cookie == null || string.IsNullOrEmpty(cookie.Value))
                {
                  return null;
                }
                //解密cookie值
                FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
                if(ticket == null || string.IsNullOrEmpty(ticket.UserData))          
                {
                  return null;            
                }
                IUserData userData = JsonConvert.DeserializeObject<TUserData>(ticket.UserData);       
                return new Principal(ticket, userData);
              }
            }
            在登錄時(shí),我們可以類(lèi)似這樣處理:
            public ActionResult Login(string userName,string password)
            {
              //驗(yàn)證用戶(hù)名和密碼等一些邏輯... 
              UserData userData = new UserData()
              {
                UserName = userName,
                UserID = userID,
                UserRole = "Admin"
              };
              HttpFormsAuthentication.SetAuthenticationCookie(userName, userData, 7);
              //驗(yàn)證通過(guò)...
            }
            登錄成功后,就會(huì)把信息寫(xiě)入cookie,可以通過(guò)瀏覽器觀察請(qǐng)求,就會(huì)有一個(gè)名稱(chēng)為"Form"的Cookie(還需要簡(jiǎn)單配置一下配置文件),它的值是一個(gè)加密后的字符串,后續(xù)的請(qǐng)求根據(jù)此cookie請(qǐng)求進(jìn)行驗(yàn)證。具體做法是在HttpApplication的AuthenticateRequest驗(yàn)證事件中調(diào)用上面的TryParsePrincipal,如:
            protected void Application_AuthenticateRequest(object sender, EventArgs e)
            {
              HttpContext.Current.User = HttpFormsAuthentication.TryParsePrincipal<UserData>(HttpContext.Current);
            }
            這里如果驗(yàn)證不通過(guò),HttpContext.Current.User就是null,表示當(dāng)前用戶(hù)未標(biāo)識(shí)。但在這里還不能做任何關(guān)于權(quán)限的處理,因?yàn)樯厦嬲f(shuō)到的,有些頁(yè)面是允許匿名訪(fǎng)問(wèn)的。
            三、AuthorizeAttribute
            這是一個(gè)Filter,在Action執(zhí)行前執(zhí)行,它實(shí)現(xiàn)了IActionFilter接口。關(guān)于Filter,可以看我之前的這篇文章,這里就不多介紹了。我們定義一個(gè)RequestAuthorizeAttribute繼承AuthorizeAttribute,并重寫(xiě)它的OnAuthorization方法,如果一個(gè)Controller或者Action標(biāo)記了該特性,那么該方法就會(huì)在Action執(zhí)行前被執(zhí)行,在這里判斷是否已經(jīng)登錄和是否有權(quán)限,如果沒(méi)有則做出相應(yīng)處理。具體代碼如下:
            [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
            public class RequestAuthorizeAttribute : AuthorizeAttribute
            {
              //驗(yàn)證
              public override void OnAuthorization(AuthorizationContext context)
              {
                EnsureHelper.EnsureNotNull(context, "httpContent");      
                //是否允許匿名訪(fǎng)問(wèn)
                if (context.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), false))
                {
                  return;
                }
                //登錄驗(yàn)證
                Principal principal = context.HttpContext.User as Principal;
                if (principal == null)
                {
                  SetUnAuthorizedResult(context);
                  HandleUnauthorizedRequest(context);
                  return;
                }
                //權(quán)限驗(yàn)證
                if (!principal.IsInRole(base.Roles) || !principal.IsInUser(base.Users))
                {
                  SetUnAuthorizedResult(context);
                  HandleUnauthorizedRequest(context);
                  return;
                }
                //驗(yàn)證配置文件
                if(!ValidateAuthorizeConfig(principal, context))
                {
                  SetUnAuthorizedResult(context);
                  HandleUnauthorizedRequest(context);
                  return;
                }      
              }
              //驗(yàn)證不通過(guò)時(shí)
              private void SetUnAuthorizedResult(AuthorizationContext context)
              {
                HttpRequestBase request = context.HttpContext.Request;
                if (request.IsAjaxRequest())
                {
                  //處理ajax請(qǐng)求
                  string result = JsonConvert.SerializeObject(JsonModel.Error(403));        
                  context.Result = new ContentResult() { Content = result };
                }
                else
                {
                  //跳轉(zhuǎn)到登錄頁(yè)面
                  string loginUrl = FormsAuthentication.LoginUrl + "?ReturnUrl=" + preUrl;
                  context.Result = new RedirectResult(loginUrl);
                }
              }
            //override
              protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
              {
                if(filterContext.Result != null)
                {
                  return;
                }
                base.HandleUnauthorizedRequest(filterContext);
              }
            }
            注:這里的代碼摘自個(gè)人項(xiàng)目中的,簡(jiǎn)寫(xiě)了部分代碼,有些是輔助類(lèi),代碼沒(méi)有貼出,但應(yīng)該不影響閱讀。
            1. 如果我們?cè)贖ttpApplication的AuthenticateRequest事件中獲得的IPrincipal為null,那么驗(yàn)證不通過(guò)。
            2. 如果驗(yàn)證通過(guò),程序會(huì)進(jìn)行驗(yàn)證AuthorizeAttribute的Roles和User屬性。
            3. 如果驗(yàn)證通過(guò),程序會(huì)驗(yàn)證配置文件中對(duì)應(yīng)的Roles和Users屬性。
            驗(yàn)證配置文件的方法如下:
              private bool ValidateAuthorizeConfig(Principal principal, AuthorizationContext context)
              {
                //action可能有重載,重載時(shí)應(yīng)該標(biāo)記ActionName區(qū)分
                ActionNameAttribute actionNameAttr = context.ActionDescriptor
                  .GetCustomAttributes(typeof(ActionNameAttribute), false)
                  .OfType<ActionNameAttribute>().FirstOrDefault();
                string actionName = actionNameAttr == null ? null : actionNameAttr.Name;
                AuthorizationConfig ac = ParseAuthorizeConfig(actionName, context.RouteData);
                if (ac != null)
                {
                  if (!principal.IsInRole(ac.Roles))
                  {
                    return false;
                  }
                  if (!principal.IsInUser(ac.Users))
                  {
                    return false;
                  }
                }
                return true;
              }
              private AuthorizationConfig ParseAuthorizeConfig(string actionName, RouteData routeData)
              {
                string areaName = routeData.DataTokens["area"] as string;
                string controllerName = null;
                object controller, action;
                if(string.IsNullOrEmpty(actionName))
                {
                  if(routeData.Values.TryGetValue("action", out action))
                  {
                    actionName = action.ToString();
                  }
                }
                if (routeData.Values.TryGetValue("controller", out controller))
                {
                  controllerName = controller.ToString();
                }
                if(!string.IsNullOrEmpty(controllerName) && !string.IsNullOrEmpty(actionName))
                {
                  return AuthorizationConfig.ParseAuthorizationConfig(
                    areaName, controllerName, actionName);
                }
                return null;
              }
            }
            可以看到,它會(huì)根據(jù)當(dāng)前請(qǐng)求的area、controller和action名稱(chēng),通過(guò)一個(gè)AuthorizationConfig類(lèi)進(jìn)行驗(yàn)證,該類(lèi)的定義如下:
            public class AuthorizationConfig
            {
              public string Roles { get; set; }
              public string Users { get; set; }
              private static XDocument _doc;
              //配置文件路徑
              private static string _path = "~/Identity/Authorization.xml";
              //首次使用加載配置文件
              static AuthorizationConfig()
              {
                string absPath = HttpContext.Current.Server.MapPath(_path);
                if (File.Exists(absPath))
                {
                  _doc = XDocument.Load(absPath);
                }
              }
              //解析配置文件,獲得包含Roles和Users的信息
              public static AuthorizationConfig ParseAuthorizationConfig(string areaName, string controllerName, string actionName)
              {
                EnsureHelper.EnsureNotNullOrEmpty(controllerName, "controllerName");
                EnsureHelper.EnsureNotNullOrEmpty(actionName, "actionName");
                if (_doc == null)
                {
                  return null;
                }
                XElement rootElement = _doc.Element("root");
                if (rootElement == null)
                {
                  return null;
                }
                AuthorizationConfig info = new AuthorizationConfig();
                XElement rolesElement = null;
                XElement usersElement = null;
                XElement areaElement = rootElement.Elements("area")
                  .Where(e => CompareName(e, areaName)).FirstOrDefault();
                XElement targetElement = areaElement ?? rootElement;
                XElement controllerElement = targetElement.Elements("controller")
                  .Where(e => CompareName(e, controllerName)).FirstOrDefault();
                //如果沒(méi)有area節(jié)點(diǎn)和controller節(jié)點(diǎn)則返回null
                if (areaElement == null && controllerElement == null)
                {
                  return null;
                }
                //此時(shí)獲取標(biāo)記的area
                if (controllerElement == null)
                {
                  rootElement = areaElement.Element("roles");
                  usersElement = areaElement.Element("users");
                }
                else
                {
                  XElement actionElement = controllerElement.Elements("action")
                    .Where(e => CompareName(e, actionName)).FirstOrDefault();
                  if (actionElement != null)
                  {
                    //此時(shí)獲取標(biāo)記action的
                    rolesElement = actionElement.Element("roles");
                    usersElement = actionElement.Element("users");
                  }
                  else
                  {
                    //此時(shí)獲取標(biāo)記controller的
                    rolesElement = controllerElement.Element("roles");
                    usersElement = controllerElement.Element("users");
                  }
                }
                info.Roles = rolesElement == null ? null : rolesElement.Value;
                info.Users = usersElement == null ? null : usersElement.Value;
                return info;
              }
              private static bool CompareName(XElement e, string value)
              {
                XAttribute attribute = e.Attribute("name");
                if (attribute == null || string.IsNullOrEmpty(attribute.Value))
                {
                  return false;
                }
                return attribute.Value.Equals(value, StringComparison.OrdinalIgnoreCase);
              }
            }
            這里的代碼比較長(zhǎng),但主要邏輯就是解析文章開(kāi)頭的配置信息。
            簡(jiǎn)單總結(jié)一下程序?qū)崿F(xiàn)的步驟:
            1. 校對(duì)用戶(hù)名和密碼正確后,調(diào)用SetAuthenticationCookie將一些狀態(tài)信息寫(xiě)入cookie。
            2. 在HttpApplication的Authentication事件中,調(diào)用TryParsePrincipal獲得狀態(tài)信息。
            3. 在需要驗(yàn)證的Action(或Controller)標(biāo)記 RequestAuthorizeAttribute特性,并設(shè)置Roles和Users;Roles和Users也可以在配置文件中配置。
            4. 在RequestAuthorizeAttribute的OnAuthorization方法中進(jìn)行驗(yàn)證和權(quán)限邏輯處理。
            四、總結(jié)
            上面就是整個(gè)登錄認(rèn)證的核心實(shí)現(xiàn)過(guò)程,只需要簡(jiǎn)單配置一下就可以實(shí)現(xiàn)了。但實(shí)際項(xiàng)目中從用戶(hù)注冊(cè)到用戶(hù)管理整個(gè)過(guò)程是比較復(fù)雜的,而且涉及到前后端驗(yàn)證、加解密問(wèn)題。關(guān)于安全問(wèn)題,F(xiàn)ormsAuthentication在加密的時(shí)候,會(huì)根據(jù)服務(wù)器的MachineKey等一些信息進(jìn)行加密,所以相對(duì)安全。當(dāng)然,如果說(shuō)請(qǐng)求被惡意攔截,然后被偽造登錄還是有可能的,這是后面要考慮的問(wèn)題了,例如使用安全的http協(xié)議https。
            以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。