亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > .NET > 正文

詳解ASP.NET MVC Form表單驗證

2024-07-10 13:30:06
字體:
來源:轉載
供稿:網友
這篇文章主要為大家詳細介紹了ASP.NET MVC Form表單驗證,一般驗證方式有Windows驗證和表單驗證,web項目用得更多的是表單驗證,感興趣的小伙伴們可以參考一下
 

一、前言

  關于表單驗證,已經有不少的文章,相信Web開發人員也都基本寫過,最近在一個個人項目中剛好用到,在這里與大家分享一下。本來想從用戶注冊開始寫起,但發現東西比較多,涉及到界面、前端驗證、前端加密、后臺解密、用戶密碼Hash、權限驗證等等,文章寫起來可能會很長,所以這里主要介紹的是登錄驗證和權限控制部分,有興趣的朋友歡迎一起交流。

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

  我們先看一下最后要實現的效果:

1.這是在Action級別的控制。

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級別的控制。當然,如果某個Action需要匿名訪問,也是允許的,因為控制級別上,Action優先級大于Controller。

//Controller級別的權限控制[RequestAuthorize(User="張三")]public class Home2Controller : Controller{  //登錄用戶訪問  public ActionResult Index()  {    return View();  }  //允許匿名訪問  [AllowAnonymous]  public ActionResult Index2()  {    return View();  }}

3.Area級別的控制。有時候我們會把一些模塊做成分區,當然這里也可以在Area的Controller和Action進行標記。

  從上面可以看到,我們需要在各個地方進行標記權限,如果把Roles和Users硬寫在程序中,不是很好的做法。我希望能更簡單一點,在配置文件進行說明。例如如下配置:

<?xml version="1.0" encoding="utf-8" ?><!--  1.這里可以把權限控制轉移到配置文件,這樣就不用在程序中寫roles和users了  2.如果程序也寫了,那么將覆蓋配置文件的。  3.action級別的優先級 > controller級別 > Area級別  --><root> <!--area級別--> <area name="Admin">  <roles>Admin</roles> </area>   <!--controller級別--> <controller name="Home2">  <user>張三</user> </controller>   <!--action級別--> <controller name="Home1">  <action name="Inde3">   <users>張三</users>  </action>  <action name="Index4">   <roles>Admin</roles>  </action> </controller></root>

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

二、主要接口

先看兩個主要用到的接口。

IPrincipal 定義了用戶對象的基本功能,接口定義如下:

public interface IPrincipal{  //標識對象  IIdentity Identity { get; }  //判斷當前角色是否屬于指定的角色  bool IsInRole(string role);}

它有兩個主要成員,IsInRole用于判斷當前對象是否屬于指定角色的,IIdentity定義了標識對象信息。HttpContext的User屬性就是IPrincipal類型的。

IIdentity 定義了標識對象的基本功能,接口定義如下:

public interface IIdentity{    //身份驗證類型  string AuthenticationType { get; }  //是否驗證通過  bool IsAuthenticated { get; }   //用戶名  string Name { get; }}

IIdentity包含了一些用戶信息,但有時候我們需要存儲更多信息,例如用戶ID、用戶角色等,這些信息會被序列到cookie中加密保存,驗證通過時可以解碼再反序列化獲得,狀態得以保存。例如定義一個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實現了IUserData接口,該接口定義了兩個方法:IsInRole和IsInUser,分別用于判斷當前用戶角色和用戶名是否符合要求。該接口定義如下:

public interface IUserData{  bool IsInRole(string role);  bool IsInUser(string user);}  接下來定義一個Principal實現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,這樣很容易更換一個UserData而不影響其它代碼。Principal的IsInRole和IsInUser間接調用了IUserData的同名方法。

三、寫入cookie和讀取cookie

  接下來,需要做的就是用戶登錄成功后,創建UserData,序列化,再利用FormsAuthentication加密,寫到cookie中;而請求到來時,需要嘗試將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);     //創建用戶票據    double tickekDays = rememberDays == 0 ? 7 : rememberDays;    var ticket = new FormsAuthenticationTicket(2, userName,      DateTime.Now, DateTime.Now.AddDays(tickekDays), false, userJson);     //FormsAuthentication提供web forms身份驗證服務    //加密    string encryptValue = FormsAuthentication.Encrypt(ticket);     //創建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);  }}

  在登錄時,我們可以類似這樣處理:

public ActionResult Login(string userName,string password){  //驗證用戶名和密碼等一些邏輯...    UserData userData = new UserData()  {    UserName = userName,    UserID = userID,    UserRole = "Admin"  };  HttpFormsAuthentication.SetAuthenticationCookie(userName, userData, 7);     //驗證通過...}

  登錄成功后,就會把信息寫入cookie,可以通過瀏覽器觀察請求,就會有一個名稱為"Form"的Cookie(還需要簡單配置一下配置文件),它的值是一個加密后的字符串,后續的請求根據此cookie請求進行驗證。具體做法是在HttpApplication的AuthenticateRequest驗證事件中調用上面的TryParsePrincipal,如:

protected void Application_AuthenticateRequest(object sender, EventArgs e){  HttpContext.Current.User = HttpFormsAuthentication.TryParsePrincipal<UserData>(HttpContext.Current);}

  這里如果驗證不通過,HttpContext.Current.User就是null,表示當前用戶未標識。但在這里還不能做任何關于權限的處理,因為上面說到的,有些頁面是允許匿名訪問的。

三、AuthorizeAttribute

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

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]public class RequestAuthorizeAttribute : AuthorizeAttribute{  //驗證  public override void OnAuthorization(AuthorizationContext context)  {    EnsureHelper.EnsureNotNull(context, "httpContent");          //是否允許匿名訪問    if (context.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), false))    {      return;    }    //登錄驗證    Principal principal = context.HttpContext.User as Principal;    if (principal == null)    {      SetUnAuthorizedResult(context);      HandleUnauthorizedRequest(context);      return;    }    //權限驗證    if (!principal.IsInRole(base.Roles) || !principal.IsInUser(base.Users))    {      SetUnAuthorizedResult(context);      HandleUnauthorizedRequest(context);      return;    }    //驗證配置文件    if(!ValidateAuthorizeConfig(principal, context))    {      SetUnAuthorizedResult(context);      HandleUnauthorizedRequest(context);      return;    }        }   //驗證不通過時  private void SetUnAuthorizedResult(AuthorizationContext context)  {    HttpRequestBase request = context.HttpContext.Request;    if (request.IsAjaxRequest())    {      //處理ajax請求      string result = JsonConvert.SerializeObject(JsonModel.Error(403));              context.Result = new ContentResult() { Content = result };    }    else    {      //跳轉到登錄頁面      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);  }}

  注:這里的代碼摘自個人項目中的,簡寫了部分代碼,有些是輔助類,代碼沒有貼出,但應該不影響閱讀。

  1. 如果我們在HttpApplication的AuthenticateRequest事件中獲得的IPrincipal為null,那么驗證不通過。

  2. 如果驗證通過,程序會進行驗證AuthorizeAttribute的Roles和User屬性。

  3. 如果驗證通過,程序會驗證配置文件中對應的Roles和Users屬性。

  驗證配置文件的方法如下:

  private bool ValidateAuthorizeConfig(Principal principal, AuthorizationContext context)  {    //action可能有重載,重載時應該標記ActionName區分    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;  }}

  可以看到,它會根據當前請求的area、controller和action名稱,通過一個AuthorizationConfig類進行驗證,該類的定義如下:

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節點和controller節點則返回null    if (areaElement == null && controllerElement == null)    {      return null;    }    //此時獲取標記的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)      {        //此時獲取標記action的        rolesElement = actionElement.Element("roles");        usersElement = actionElement.Element("users");      }      else      {        //此時獲取標記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);  }}

這里的代碼比較長,但主要邏輯就是解析文章開頭的配置信息。

簡單總結一下程序實現的步驟:

  1. 校對用戶名和密碼正確后,調用SetAuthenticationCookie將一些狀態信息寫入cookie。

  2. 在HttpApplication的Authentication事件中,調用TryParsePrincipal獲得狀態信息。

  3. 在需要驗證的Action(或Controller)標記 RequestAuthorizeAttribute特性,并設置Roles和Users;Roles和Users也可以在配置文件中配置。

  4. 在RequestAuthorizeAttribute的OnAuthorization方法中進行驗證和權限邏輯處理。

四、總結

  上面就是整個登錄認證的核心實現過程,只需要簡單配置一下就可以實現了。但實際項目中從用戶注冊到用戶管理整個過程是比較復雜的,而且涉及到前后端驗證、加解密問題。關于安全問題,FormsAuthentication在加密的時候,會根據服務器的MachineKey等一些信息進行加密,所以相對安全。當然,如果說請求被惡意攔截,然后被偽造登錄還是有可能的,這是后面要考慮的問題了,例如使用安全的http協議https。

以上就是本文的全部內容,希望對大家的學習有所幫助。



發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲xxxxx| 亚洲第一视频网站| 国产精品久久久久久久久久| 欧美一级片在线播放| 日韩成人在线网站| 日韩成人激情视频| 亚洲精品wwwww| 国产不卡av在线免费观看| 日韩av在线不卡| 国内外成人免费激情在线视频网站| 国产精品美女免费| 亚洲精品97久久| 欧美高清一级大片| 欧美乱妇高清无乱码| 国产精品久久久久久中文字| 琪琪亚洲精品午夜在线| www.美女亚洲精品| 精品视频在线播放免| 国产精品稀缺呦系列在线| 欧美最顶级的aⅴ艳星| 国产精品小说在线| 欧美孕妇与黑人孕交| 精品高清一区二区三区| 亚洲日韩欧美视频| 日韩av在线影院| 亚洲国产精品福利| 久久男人的天堂| 在线精品播放av| 91麻豆国产语对白在线观看| 北条麻妃在线一区二区| 精品一区精品二区| 91夜夜未满十八勿入爽爽影院| 欧美黄色免费网站| 国内精品视频一区| 精品综合久久久久久97| 在线播放日韩专区| 97香蕉久久夜色精品国产| 97久久国产精品| 91精品久久久久久久久久久| 亚洲美女福利视频网站| 俺去了亚洲欧美日韩| 欧美福利视频网站| 91日本视频在线| 九九精品在线播放| 91极品视频在线| 2023亚洲男人天堂| 91人人爽人人爽人人精88v| 国产成人一区二区三区电影| 亚洲影院在线看| 国产精品激情av电影在线观看| 国产精品免费久久久久影院| 久久精品国产亚洲一区二区| 国产主播在线一区| 98午夜经典影视| 欧美成人免费全部| 国产精品日韩电影| 亚洲护士老师的毛茸茸最新章节| 一区二区在线视频| 亚洲石原莉奈一区二区在线观看| 中文字幕综合一区| 精品福利视频导航| 国产香蕉97碰碰久久人人| 欧美激情精品久久久久久变态| 91日本在线视频| 欧美日本中文字幕| 日韩亚洲成人av在线| 黑人欧美xxxx| 一区国产精品视频| 久久久天堂国产精品女人| 国产精品久久久久久av下载红粉| 亚洲图片欧美日产| 九九热精品视频国产| 久久精品国产一区二区电影| 国产精品美女在线观看| 国产精品视频一区二区三区四| 日韩亚洲国产中文字幕| 欧美精品在线播放| 欧美电影免费观看高清完整| 日韩在线一区二区三区免费视频| 亲子乱一区二区三区电影| 欧美日韩在线一区| 国产午夜精品理论片a级探花| 亚洲影院色无极综合| 亚洲欧美自拍一区| 不卡av电影在线观看| 欧美国产第一页| 欧美视频中文在线看| 一个人www欧美| 亚洲理论在线a中文字幕| 久久综合久中文字幕青草| 欧美日韩免费网站| 亚洲理论在线a中文字幕| 亲爱的老师9免费观看全集电视剧| 91免费国产网站| 成人亚洲欧美一区二区三区| 日韩av中文字幕在线| 色狠狠av一区二区三区香蕉蜜桃| 日韩欧美成人网| 欧美有码在线视频| 国产亚洲xxx| 日韩av电影手机在线观看| 中文字幕亚洲一区在线观看| 欧美一级视频一区二区| 久久精品国产99国产精品澳门| 久久久免费观看视频| 久久免费视频网站| 国产精品成人观看视频国产奇米| 亚洲电影av在线| 亚洲国产精品一区二区三区| 久久人体大胆视频| 日韩中文字幕国产| 国产精品美女www爽爽爽视频| 亚洲第一二三四五区| 91国偷自产一区二区三区的观看方式| 成人h片在线播放免费网站| 国产日韩在线看| 欧美高清在线观看| 国产精品香蕉av| 久久久999精品免费| 国产亚洲精品美女| 亚洲精品美女免费| 一区二区三区回区在观看免费视频| 亚洲永久免费观看| 国产精品亚洲视频在线观看| 美女少妇精品视频| 国产在线一区二区三区| 中文字幕国产亚洲2019| 日韩精品有码在线观看| 亚洲国产精品久久| www.日韩.com| 国产精品欧美激情| 日韩av片永久免费网站| 亚洲精品国精品久久99热| 欧美成人免费播放| 精品国产拍在线观看| 欧美亚洲另类在线| 亚洲欧洲第一视频| 国产一区二区黑人欧美xxxx| 欧美巨猛xxxx猛交黑人97人| 国内揄拍国内精品少妇国语| 国产成人av在线播放| 欧美成人在线影院| 精品久久久久久久中文字幕| 欧美一区二区三区四区在线| 久久99视频精品| 韩国欧美亚洲国产| 性欧美激情精品| 亚洲天堂免费视频| 中文字幕精品—区二区| 97国产一区二区精品久久呦| 欧美成人免费全部观看天天性色| 日本中文字幕久久看| 岛国av一区二区| 午夜精品一区二区三区在线视| 国产精品香蕉在线观看| 亚洲美女久久久| 亚洲精品一区中文字幕乱码| 亚洲国产精品系列| 久久精品国产99国产精品澳门| 亚洲免费一级电影| 亚洲欧美在线免费| 亚洲一级免费视频| 成人一区二区电影| 亚洲欧美激情四射在线日|