茄子在线看片免费人成视频,午夜福利精品a在线观看,国产高清自产拍在线观看,久久综合久久狠狠综合

    <s id="ddbnn"></s>
  • <sub id="ddbnn"><ol id="ddbnn"></ol></sub>

  • <legend id="ddbnn"></legend><s id="ddbnn"></s>

    詳解ASP.NET MVC Form表單驗(yàn)證
    來源:易賢網(wǎng) 閱讀:1242 次 日期:2016-08-10 15:41:48
    溫馨提示:易賢網(wǎng)小編為您整理了“詳解ASP.NET MVC Form表單驗(yàn)證”,方便廣大網(wǎng)友查閱!

    這篇文章主要為大家詳細(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開發(fā)人員也都基本寫過,最近在一個(gè)個(gè)人項(xiàng)目中剛好用到,在這里與大家分享一下。本來想從用戶注冊(cè)開始寫起,但發(fā)現(xiàn)東西比較多,涉及到界面、前端驗(yàn)證、前端加密、后臺(tái)解密、用戶密碼Hash、權(quán)限驗(yàn)證等等,文章寫起來可能會(huì)很長(zhǎng),所以這里主要介紹的是登錄驗(yàn)證和權(quán)限控制部分,有興趣的朋友歡迎一起交流。

    一般驗(yàn)證方式有Windows驗(yàn)證和表單驗(yàn)證,web項(xiàng)目用得更多的是表單驗(yàn)證。原理很簡(jiǎn)單,簡(jiǎn)單地說就是利用瀏覽器的cookie,將驗(yàn)證令牌存儲(chǔ)在客戶端瀏覽器上,cookie每次會(huì)隨請(qǐng)求發(fā)送到服務(wù)器,服務(wù)器驗(yàn)證這個(gè)令牌。通常一個(gè)系統(tǒng)的用戶會(huì)分為多種角色:匿名用戶、普通用戶和管理員;這里面又可以再細(xì)分,例如用戶可以是普通用戶或Vip用戶,管理員可以是普通管理員或超級(jí)管理員等。在項(xiàng)目中,我們有的頁(yè)面可能只允許管理員查看,有的只允許登錄用戶查看,這就是角色區(qū)分(Roles);某些特別情況下,有些頁(yè)面可能只允許叫“張三”名字的人查看,這就是用戶區(qū)分(Users)。

    我們先看一下最后要實(shí)現(xiàn)的效果:

    1.這是在Action級(jí)別的控制。

    public class Home1Controller : Controller

    {

      //匿名訪問

      public ActionResult Index()

      {

        return View();

      }

      //登錄用戶訪問

      [RequestAuthorize]

      public ActionResult Index2()

      {

        return View();

      }

      //登錄用戶,張三才能訪問

      [RequestAuthorize(Users="張三")]

      public ActionResult Index3()

      {

        return View();

      }

      //管理員訪問

      [RequestAuthorize(Roles="Admin")]

      public ActionResult Index4()

      {

        return View();

      }

    }

    2.這是在Controller級(jí)別的控制。當(dāng)然,如果某個(gè)Action需要匿名訪問,也是允許的,因?yàn)榭刂萍?jí)別上,Action優(yōu)先級(jí)大于Controller。

    //Controller級(jí)別的權(quán)限控制

    [RequestAuthorize(User="張三")]

    public class Home2Controller : Controller

    {

      //登錄用戶訪問

      public ActionResult Index()

      {

        return View();

      }

      //允許匿名訪問

      [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硬寫在程序中,不是很好的做法。我希望能更簡(jiǎn)單一點(diǎn),在配置文件進(jìn)行說明。例如如下配置:

    <?xml version="1.0" encoding="utf-8" ?>

    <!--

      1.這里可以把權(quán)限控制轉(zhuǎn)移到配置文件,這樣就不用在程序中寫roles和users了

      2.如果程序也寫了,那么將覆蓋配置文件的。

      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>

    寫在配置文件里,是為了方便管理,如果程序里也寫了,將覆蓋配置文件的。ok,下面進(jìn)入正題。

    二、主要接口

    先看兩個(gè)主要用到的接口。

    IPrincipal 定義了用戶對(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類型的。

    IIdentity 定義了標(biāo)識(shí)對(duì)象的基本功能,接口定義如下:

    public interface IIdentity

    {  

      //身份驗(yàn)證類型

      string AuthenticationType { get; }

      //是否驗(yàn)證通過

      bool IsAuthenticated { get; } 

      //用戶名

      string Name { get; }

    }

    IIdentity包含了一些用戶信息,但有時(shí)候我們需要存儲(chǔ)更多信息,例如用戶ID、用戶角色等,這些信息會(huì)被序列到cookie中加密保存,驗(yàn)證通過時(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)前用戶角色和用戶名是否符合要求。該接口定義如下:

    public interface IUserData

    {

      bool IsInRole(string role);

      bool IsInUser(string user);

    }

    接下來定義一個(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的同名方法。

    三、寫入cookie和讀取cookie

    接下來,需要做的就是用戶登錄成功后,創(chuàng)建UserData,序列化,再利用FormsAuthentication加密,寫到cookie中;而請(qǐng)求到來時(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)建用戶票據(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í),我們可以類似這樣處理:

    public ActionResult Login(string userName,string password)

    {

      //驗(yàn)證用戶名和密碼等一些邏輯... 

      UserData userData = new UserData()

      {

        UserName = userName,

        UserID = userID,

        UserRole = "Admin"

      };

      HttpFormsAuthentication.SetAuthenticationCookie(userName, userData, 7);

      //驗(yàn)證通過...

    }

    登錄成功后,就會(huì)把信息寫入cookie,可以通過瀏覽器觀察請(qǐng)求,就會(huì)有一個(gè)名稱為"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)證不通過,HttpContext.Current.User就是null,表示當(dāng)前用戶未標(biāo)識(shí)。但在這里還不能做任何關(guān)于權(quán)限的處理,因?yàn)樯厦嬲f到的,有些頁(yè)面是允許匿名訪問的。

    三、AuthorizeAttribute

    這是一個(gè)Filter,在Action執(zhí)行前執(zhí)行,它實(shí)現(xiàn)了IActionFilter接口。關(guān)于Filter,可以看我之前的這篇文章,這里就不多介紹了。我們定義一個(gè)RequestAuthorizeAttribute繼承AuthorizeAttribute,并重寫它的OnAuthorization方法,如果一個(gè)Controller或者Action標(biāo)記了該特性,那么該方法就會(huì)在Action執(zhí)行前被執(zhí)行,在這里判斷是否已經(jīng)登錄和是否有權(quán)限,如果沒有則做出相應(yīng)處理。具體代碼如下:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]

    public class RequestAuthorizeAttribute : AuthorizeAttribute

    {

      //驗(yàn)證

      public override void OnAuthorization(AuthorizationContext context)

      {

        EnsureHelper.EnsureNotNull(context, "httpContent");      

        //是否允許匿名訪問

        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)證不通過時(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)寫了部分代碼,有些是輔助類,代碼沒有貼出,但應(yīng)該不影響閱讀。

    1. 如果我們?cè)贖ttpApplication的AuthenticateRequest事件中獲得的IPrincipal為null,那么驗(yàn)證不通過。

    2. 如果驗(yàn)證通過,程序會(huì)進(jìn)行驗(yàn)證AuthorizeAttribute的Roles和User屬性。

    3. 如果驗(yàn)證通過,程序會(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名稱,通過一個(gè)AuthorizationConfig類進(jìn)行驗(yàn)證,該類的定義如下:

    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();

        //如果沒有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),但主要邏輯就是解析文章開頭的配置信息。

    簡(jiǎn)單總結(jié)一下程序?qū)崿F(xiàn)的步驟:

    1. 校對(duì)用戶名和密碼正確后,調(diào)用SetAuthenticationCookie將一些狀態(tài)信息寫入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)過程,只需要簡(jiǎn)單配置一下就可以實(shí)現(xiàn)了。但實(shí)際項(xiàng)目中從用戶注冊(cè)到用戶管理整個(gè)過程是比較復(fù)雜的,而且涉及到前后端驗(yàn)證、加解密問題。關(guān)于安全問題,F(xiàn)ormsAuthentication在加密的時(shí)候,會(huì)根據(jù)服務(wù)器的MachineKey等一些信息進(jìn)行加密,所以相對(duì)安全。當(dāng)然,如果說請(qǐng)求被惡意攔截,然后被偽造登錄還是有可能的,這是后面要考慮的問題了,例如使用安全的http協(xié)議https。

    以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。

    更多信息請(qǐng)查看網(wǎng)絡(luò)編程
    易賢網(wǎng)手機(jī)網(wǎng)站地址:詳解ASP.NET MVC Form表單驗(yàn)證
    由于各方面情況的不斷調(diào)整與變化,易賢網(wǎng)提供的所有考試信息和咨詢回復(fù)僅供參考,敬請(qǐng)考生以權(quán)威部門公布的正式信息和咨詢?yōu)闇?zhǔn)!

    2026上岸·考公考編培訓(xùn)報(bào)班

    • 報(bào)班類型
    • 姓名
    • 手機(jī)號(hào)
    • 驗(yàn)證碼
    關(guān)于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡(jiǎn)要咨詢 | 簡(jiǎn)要咨詢須知 | 新媒體/短視頻平臺(tái) | 手機(jī)站點(diǎn) | 投訴建議
    工業(yè)和信息化部備案號(hào):滇ICP備2023014141號(hào)-1 云南省教育廳備案號(hào):云教ICP備0901021 滇公網(wǎng)安備53010202001879號(hào) 人力資源服務(wù)許可證:(云)人服證字(2023)第0102001523號(hào)
    云南網(wǎng)警備案專用圖標(biāo)
    聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢關(guān)注公眾號(hào):hfpxwx
    咨詢QQ:1093837350(9:00—18:00)版權(quán)所有:易賢網(wǎng)
    云南網(wǎng)警報(bào)警專用圖標(biāo)