七天學會ASP.NET MVC (四)——用戶授權認證問題

實驗15——有關錯誤驗證的保留值

在上一節的實驗13,我們介紹了服務器端的身份驗證,實驗14中添加了客戶端驗證的支持,希望每位讀者都能夠把實驗14理解透徹,逐行代碼查看,保證每行代碼都理解了,這樣才有助於理解我們接下來的實驗。

實驗15中將學習如何在驗證失敗時,填充值。

1. 創建 CreateEmployeeViewModel 類。

在ViewModel文件夾下,新建類:

public class CreateEmployeeViewModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Salary { get; set; }
}

2. 修改  SaveEmployee 方法

爲了重新生成,重用Model Binder創建的 Employee 對象,修改 SaveEmployee 方法。

   1:  public ActionResult SaveEmployee(Employee e, string BtnSubmit)
   2:  {
   3:      switch (BtnSubmit)
   4:      {
   5:          case "Save Employee":
   6:              if (ModelState.IsValid)
   7:              {
   8:                  EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
   9:                  empBal.SaveEmployee(e);
  10:                  return RedirectToAction("Index");
  11:              }
  12:              else
  13:              {
  14:                  CreateEmployeeViewModel vm = new CreateEmployeeViewModel();
  15:                  vm.FirstName = e.FirstName;
  16:                  vm.LastName = e.LastName;
  17:                  if (e.Salary.HasValue)
  18:                  {
  19:                      vm.Salary = e.Salary.ToString();                        
  20:                  }
  21:                  else
  22:                  {
  23:                      vm.Salary = ModelState["Salary"].Value.AttemptedValue;                       
  24:                  }
  25:                  return View("CreateEmployee", vm); // Day 4 Change - Passing e here
  26:              }
  27:          case "Cancel":
  28:              return RedirectToAction("Index");
  29:      }
  30:      return new EmptyResult();
  31:  }

3. 填充View的值

3.1 將View設置爲強類型的View

在 CreateEmployee View文件開始添加以下代碼:

   1:  @using WebApplication1.ViewModels
   2:  @model CreateEmployeeViewModel

3.2 在響應控件中顯示Model值

   1:  ...
   2:   
   3:  ...
   4:   
   5:  ...
   6:   
   7:  <input type="text" id="TxtFName" name="FirstName" value="@Model.FirstName" />
   8:   
   9:  ...
  10:   
  11:  ...
  12:   
  13:  ...
  14:   
  15:  <input type="text" id="TxtLName" name="LastName" value="@Model.LastName" />
  16:   
  17:  ...
  18:   
  19:  ...
  20:   
  21:  ...
  22:   
  23:  <input type="text" id="TxtSalary" name="Salary" value="@Model.Salary" />
  24:   
  25:  ...
  26:   
  27:  ...
  28:   
  29:  ...

4. 運行點擊Add New鏈接

瀏覽器提示錯誤。我們在實驗講述完之後,再來解釋爲什麼會出現錯誤。

5. 修改AddNew方法

   1:  public ActionResult AddNew()
   2:  {
   3:      return View("CreateEmployee”, new CreateEmployeeViewModel());
   4:  }

6.  運行測試

測試1

  • 點擊 “Add New”跳轉到  AddNew 頁面。
  • 設置名字爲空
  • 輸入工資值 56
  • 點擊“Save Employee”按鈕。

會出現驗證失敗,但是數字 56 仍然顯示在 Salary 文本框中。

測試2

如圖所示,姓名仍然保留在文本框中,卻未保留工資,接下來我們來討論上述問題的解決辦法。

關於實驗15

是否是真的將值保留?

不是,是從post數據中重新獲取的。

爲什麼需要在初始化請求時,在Add New 方法中傳遞 new CreateEmployeeViewModel()?

View中,試着將Model中的數據重新顯示在文本框中。

如:

<input id="TxtSalary" name="Salary" type="text" value="@Model.Salary" />

如上所示,可以訪問當前Model的“First Name”屬性,如果Model 爲空,會拋出類無法實例化的異常“Object reference not set to an instance of the class”。

當點擊”Add New“超鏈接時,請求會通過Add New方法處理,在該Action 方法中,可以不傳遞任何數據。即就是,View中的Model屬性爲空。因此會拋出“Object reference not set to an instance of the class”異常。爲了解決此問題,所以會在初始化請求時,傳”new CreateEmployeeViewModel()“。

上述的這些功能,有什麼方法可以自動生成?

使用HTML 幫助類就可以實現。在實驗16中我們會講解HTML 幫助類。

實驗16——添加客戶端驗證

首先了解,需要驗證什麼?

1. FirstName 不能爲空

2. LastName字符長度不能大於5

3. Salary不能爲空,且應該爲數字類型

4. FirstName 不能包含@字符

接下來,實現客戶端驗證功能

1. 創建JavaScript 驗證文件

在Script文件下,新建JavaScript文件,命名爲“Validations.js”

2. 創建驗證函數

在“Validations.js”文件中創建驗證函數:

   1:  function IsFirstNameEmpty() {
   2:      if (document.getElementById('TxtFName').value == "") {
   3:          return 'First Name should not be empty';
   4:      }
   5:      else { return ""; }
   6:  }
   7:   
   8:  function IsFirstNameInValid() {    
   9:      if (document.getElementById('TxtFName').value.indexOf("@") != -1) {
  10:          return 'First Name should not contain @';
  11:      }
  12:      else { return ""; }
  13:  }
  14:  function IsLastNameInValid() {
  15:      if (document.getElementById('TxtLName').value.length>=5) {
  16:          return 'Last Name should not contain more than 5 character';
  17:      }
  18:      else { return ""; }
  19:  }
  20:  function IsSalaryEmpty() {
  21:      if (document.getElementById('TxtSalary').value=="") {
  22:          return 'Salary should not be empty';
  23:      }
  24:      else { return ""; }
  25:  }
  26:  function IsSalaryInValid() {
  27:      if (isNaN(document.getElementById('TxtSalary').value)) {
  28:          return 'Enter valid salary';
  29:      }
  30:      else { return ""; }
  31:  }
  32:  function IsValid() {
  33:   
  34:      var FirstNameEmptyMessage = IsFirstNameEmpty();
  35:      var FirstNameInValidMessage = IsFirstNameInValid();
  36:      var LastNameInValidMessage = IsLastNameInValid();
  37:      var SalaryEmptyMessage = IsSalaryEmpty();
  38:      var SalaryInvalidMessage = IsSalaryInValid();
  39:   
  40:      var FinalErrorMessage = "Errors:";
  41:      if (FirstNameEmptyMessage != "")
  42:          FinalErrorMessage += "\n" + FirstNameEmptyMessage;
  43:      if (FirstNameInValidMessage != "")
  44:          FinalErrorMessage += "\n" + FirstNameInValidMessage;
  45:      if (LastNameInValidMessage != "")
  46:          FinalErrorMessage += "\n" + LastNameInValidMessage;
  47:      if (SalaryEmptyMessage != "")
  48:          FinalErrorMessage += "\n" + SalaryEmptyMessage;
  49:      if (SalaryInvalidMessage != "")
  50:          FinalErrorMessage += "\n" + SalaryInvalidMessage;
  51:   
  52:      if (FinalErrorMessage != "Errors:") {
  53:          alert(FinalErrorMessage);
  54:          return false;
  55:      }
  56:      else {
  57:          return true;
  58:      }
  59:  }

3. 在 “CreateEmployee”View 中添加 Validations.js文件引用:

   1:  <script src="~/Scripts/Validations.js"></script>

4.  在點擊 SaveEmployee按鈕時,調用驗證函數,如下:

<input type="submit" name="BtnSubmit" value="Save Employee" onclick="IsValid();"/>

5.  運行測試

點擊 Add New 鏈接,跳轉到 ”Add  New“頁面

測試1

測試2

關於實驗16

爲什麼在點擊”SaveEmployee “按鈕時,需要返回關鍵字?

如之前實驗9討論的,當點擊提交按鈕時,是給服務器發送請求,驗證失敗時對服務器請求沒有意義。通過添加”return false“代碼,可以取消默認的服務器請求。

在 IsValid函數將返回false,表示驗證失敗來實現預期的功能。

除了提示用戶,是否可以在當前頁面顯示錯誤信息?

是可以得,只需要爲每個錯誤創建span 標籤,默認設置爲不可見,當提交按鈕點擊時,如果驗證失敗,使用JavaScript修改錯誤的可見性。

自動獲取客戶端驗證還有什麼方法?

是,當使用Html 幫助類,可根據服務端驗證來獲取自動客戶端驗證,在以後會詳細討論。

服務器端驗證還有沒有必須使用?

在一些JavaScript腳本代碼無法使用時,服務器端可以替代使用。

實驗 17 添加授權認證

在實驗17中,會改進GetView方法,使其變得更加安全,只有合法的用戶才能夠訪問該方法。

在本系列的第一講中,我們瞭解了Asp.Net和MVC的意義,知道MVC是Asp.net的一部分,MVC繼承了ASP.NET的所有特徵,包含表單認證。

先來了解ASP.NET是如何進行Form認證的。

  1. 終端用戶在瀏覽器的幫助下,發送Form認證請求。
  2. 瀏覽器會發送存儲在客戶端的所有相關的用戶數據。
  3. 當服務器端接收到請求時,服務器會檢測請求,查看是否存在 “Authentication Cookie”的Cookie。
  4. 如果查找到認證Cookie,服務器會識別用戶,驗證用戶是否合法。
  5. 如果爲找到“Authentication Cookie”,服務器會將用戶作爲匿名(未認證)用戶處理,在這種情況下,如果請求的資源標記着 protected/secured,用戶將會重定位到登錄頁面。

1. 創建 AuthenticationController  Login 行爲方法

右擊controller文件夾,選擇添加新Controller,新建並命名爲”Authentication“即Controller的全稱爲”AuthenticationController“。

新建Login action方法:

   1:  public class AuthenticationController : Controller
   2:  {
   3:      // GET: Authentication
   4:      public ActionResult Login()
   5:      {
   6:          return View();
   7:      }
   8:  }

2. 創建Model

在Model 文件夾下新建Model,命名爲 UserDetails。

   1:  namespace WebApplication1.Models
   2:  {
   3:      public class UserDetails
   4:      {
   5:          public string UserName { get; set; }
   6:          public string Password { get; set; }
   7:      }
   8:  }

3.  創建Login View

在“~/Views/Authentication”文件夾下,新建View命名爲Login,並將UserDetails轉換爲強View類型。

在View中添加以下代碼:

   1:  @model WebApplication1.Models.UserDetails
   2:   
   3:  @{
   4:   
   5:      Layout = null;
   6:   
   7:  }
   8:   
   9:  <!DOCTYPE html>
  10:   
  11:  <html>
  12:   
  13:  <head>
  14:   
  15:      <meta name="viewport" content="width=device-width" />
  16:   
  17:      <title>Login</title>
  18:   
  19:  </head>
  20:   
  21:  <body>
  22:   
  23:      <div>
  24:   
  25:          @using (Html.BeginForm("DoLogin", "Authentication", FormMethod.Post))
  26:   
  27:          {
  28:   
  29:              @Html.LabelFor(c=>c.UserName)
  30:   
  31:              @Html.TextBoxFor(x=>x.UserName)
  32:   
  33:             
  34:   
  35:              <br />
  36:   
  37:              @Html.LabelFor(c => c.Password)
  38:   
  39:              @Html.PasswordFor(x => x.Password)
  40:   
  41:              <br />
  42:   
  43:   
  44:              <input type="submit" name="BtnSubmit" value="Login" />
  45:   
  46:          }
  47:   
  48:      </div>
  49:   
  50:  </body>
  51:   
  52:  </html>

在上述代碼中可以看出,使用HtmlHelper類在View中替代了純HTML代碼。

  • View中可使用”Html”調用HtmlHelper類
  • HtmlHelper類函數返回html字符串

示例1:

   1:  @Html.TextBoxFor(x=>x.UserName)

轉換爲HTML代碼

<input id="UserName" name="UserName" type="text" value="" />

示例2:

   1:  @using (Html.BeginForm("DoLogin", "Authentication", FormMethod.Post))
   2:  {
   3:  }

轉換爲HTML代碼:

   1:  <form action="/Authentication/DoLogin" method="post">
   2:  </form>

4. 運行測試

輸入Login action方法的URL:“http://localhost:8870/Authentication/Login”

5. 實現Form認證

打開 Web.config文件,在System.Web部分,找到Authentication的子標籤。如果不存在此標籤,就在文件中添加Authentication標籤。

設置Authentication的Mode爲Forms,loginurl設置爲”Login”方法的URL.

   1:  <authentication mode="Forms">
   2:  <forms loginurl="~/Authentication/Login"></forms>
   3:  </authentication>
 
 
6. 讓Action 方法更安全
在Index action 方法中添加認證屬性 [Authorize].
   1:  [Authorize]
   2:  public ActionResult Index()
   3:  {
   4:      EmployeeListViewModel employeeListViewModel = new EmployeeListViewModel();
   5:  ......

 

7. 運行測試,輸入 EmployeeController 的 Index action的URL:“http://localhost:8870/Employee/Index”

 
對於Index action的請求會自動重鏈接到 login action。

8. 創建業務層功能

打開 EmployeeBusinessLayer 類,新建 IsValidUser方法:

   1:  public bool IsValidUser(UserDetails u)
   2:  {
   3:      if (u.UserName == "Admin" && u.Password == "Admin")
   4:      {
   5:          return true;
   6:      }
   7:      else
   8:      {
   9:          return false;
  10:      }
  11:  }

9. 創建 DoLogin  action 方法

打開 AuthenticationController 類,新建action 方法命名爲 DoLogin。

當點擊登錄時,Dologin action 方法會被調用。

Dologin 方法的功能:

  1. 通過調用業務層功能檢測用戶是否合法。
  2. 如果是合法用戶,創建認證Cookie。可用於以後的認證請求過程中。
  3. 如果是非法用戶,給當前的ModelState添加新的錯誤信息,將錯誤信息顯示在View中。
   1:  [HttpPost]
   2:  public ActionResult DoLogin(UserDetails u)
   3:  {
   4:      EmployeeBusinessLayer bal = new EmployeeBusinessLayer();
   5:      if (bal.IsValidUser(u))
   6:      {
   7:          FormsAuthentication.SetAuthCookie(u.UserName, false);
   8:          return RedirectToAction("Index", "Employee");
   9:      }
  10:      else
  11:      {
  12:          ModelState.AddModelError("CredentialError", "Invalid Username or Password");
  13:          return View("Login");
  14:      }
  15:  }

10.在View 中顯示信息

打開Login View,在 @Html.BeginForm中 添加以下代碼

   1:  @Html.ValidationMessage("CredentialError", new {style="color:red;" })
   2:  @using (Html.BeginForm("DoLogin", "Authentication", FormMethod.Post))
   3:  {

11. 運行測試

測試1

測試2

關於實驗17

爲什麼Dologin會添加 HttpPost 屬性,還有其他類似的屬性嗎?

該屬性可使得DoLogin 方法打開Post 請求。如果有人嘗試獲取DoLogin,將不會起作用。還有很多類似的屬性如HttpGet,HttpPut和HttpDelete屬性.

FormsAuthentication.SetAuthCookie是必須寫的嗎?

是必須寫的。讓我們瞭解一些小的工作細節。

  • 客戶端通過瀏覽器給服務器發送請求。
  • 當通過瀏覽器生成,所有相關的Cookies也會隨着請求一起發送。
  • 服務器接收請求後,準備響應。
  • 請求和響應都是通過HTTP協議傳輸的,HTTP是無狀態協議。每個請求都是新請求,因此當同一客戶端發出二次請求時,服務器無法識別,爲了解決此問題,服務器會在準備好的請求包中添加一個Cookie,然後返回。
  • 當客戶端的瀏覽器接收到帶有Cookie的響應,會在客戶端創建Cookies。
  • 如果客戶端再次給服務器發送請求,服務器就會識別。

FormsAuthentication.SetAuthCookie將添加 “Authentication”特殊的Cookie來響應。

是否意味着沒有Cookies,FormsAuthentication 將不會有作用?

不是的,可以使用URI代替Cookie。

打開Web.Config文件,修改Authentication/Forms部分:

   1:  <forms cookieless="UseUri" loginurl="~/Authentication/Login"></forms>

 

授權的Cookie會使用URL傳遞。

通常情況下,Cookieless屬性會被設置爲“AutoDetect“,表示認證工作是通過Cookie完成的,是不支持URL傳遞的。

FormsAuthentication.SetAuthCookie中第二個參數”false“表示什麼?

false決定了是否創建永久有用的Cookie。臨時Cookie會在瀏覽器關閉時自動刪除,永久Cookie不會被刪除。可通過瀏覽器設置或是編寫代碼手動刪除。

當憑證錯誤時,UserName 文本框的值是如何被重置的?

HTML 幫助類會從Post 數據中獲取相關值並重置文本框的值。這是使用HTML 幫助類的一大優勢。

Authorize屬性有什麼用?

Asp.net MVC中提供四種過濾器來過濾請求和響應的,Authorize屬性是在Authorize過濾器之後執行的,可以確保授權請求Action 方法處理。

需要爲每個Action 方法添加授權屬性嗎?

不需要,可以將授權屬性添加到Controller 層或 Global 層。

實驗18——在View中顯示UserName

在本實驗中,我們會在View中顯示已登錄的用戶名

1. 在ViewModel 中添加 UserName

打開 EmployeeListViewModel,添加屬性叫:UserName。

   1:  public class EmployeeListViewModel
   2:  {
   3:      public List<EmployeeViewModel><employeeviewmodel> Employees { get; set; }
   4:      public string UserName { get; set; }
   5:  }
   6:  </employeeviewmodel>

2. 給 ViewModel  UserName 設置值

修改 EmployeeController,修改 Index 方法。

   1:  public ActionResult Index()
   2:  {
   3:      EmployeeListViewModel employeeListViewModel = new EmployeeListViewModel();
   4:      employeeListViewModel.UserName = User.Identity.Name; //New Line
   5:  ......

3.  顯示 View UserName

   1:  <body>
   2:   
   3:    <div style="text-align:right"> Hello, @Model.UserName </div>
   4:   
   5:    <hr />
   6:   
   7:    <a  href="/Employee/AddNew">Add New</a>
   8:   
   9:      <div>
  10:   
  11:         <table border="1"><span style="font-size: 9pt;"> 
  12:  </span>

4. 運行

實驗 19——實現註銷功能 

1. 創建註銷鏈接,打開Index.cshtml 創建 Logout 鏈接如下:

   1:  <body>
   2:   
   3:      <div style="text-align:right">Hello, @Model.UserName
   4:   
   5:      <a href="/Authentication/Logout">Logout</a></div>
   6:   
   7:      <hr />
   8:   
   9:      <a  href="/Employee/AddNew">Add New</a>
  10:   
  11:      <div>
  12:   
  13:          <table border="1">

2.  創建Logout Action 方法

打開 AuthenticationController 添加新的Logout action方法:

   1:  public ActionResult Logout()
   2:  {
   3:      FormsAuthentication.SignOut();
   4:      return RedirectToAction("Login");
   5:  }

3.  運行

實驗20——實現登錄頁面驗證

1. 添加 data annotation

打開  UserDetails.cs,添加  Data Annotation:

   1:  public class UserDetails
   2:  {
   3:   
   4:  [StringLength(7,MinimumLength=2, ErrorMessage = "UserName length should be between 2 and 7")]
   5:      public string UserName { get; set; }
   6:      public string Password { get; set; }
   7:  }

 

2. 在View 中顯示錯誤信息

修改 Login.cshtml能夠提示錯誤信息。

   1:  @using (Html.BeginForm("DoLogin", "Authentication", FormMethod.Post))
   2:  {
   3:      @Html.LabelFor(c=>c.UserName)
   4:      @Html.TextBoxFor(x=>x.UserName)
   5:      @Html.ValidationMessageFor(x=>x.UserName)
   6:  ......

3. 修改 DoLogin

修改 DoLogin action 方法:

   1:  [HttpPost]
   2:  public ActionResult DoLogin(UserDetails u)
   3:  {
   4:      if (ModelState.IsValid)
   5:      {
   6:          EmployeeBusinessLayer bal = new EmployeeBusinessLayer();
   7:          if (bal.IsValidUser(u))
   8:          {
   9:              FormsAuthentication.SetAuthCookie(u.UserName, false);
  10:              return RedirectToAction("Index", "Employee");
  11:          }
  12:          else
  13:          {
  14:              ModelState.AddModelError("CredentialError", "Invalid Username or Password");
  15:              return View("Login");
  16:          }
  17:      }
  18:      else
  19:      {
  20:          return View("Login");
  21:      }
  22:  }

4.  運行

實驗 21——登錄頁面實現客戶端驗證

在本實驗中介紹一種方法實現客戶端驗證

1. 下載 jQuery unobtrusive Validation文件

右擊項目,選擇“Manage Nuget packages”,點擊在線查找”jQuery Unobtrusive“,安裝”Microsoft jQuery Unobtrusive Valiadtion“

2. 在View 中添加 jQuery Validation 引用

在Scripts文件中,添加以下 JavaScript文件

  • jQuery-Someversion.js
  • jQuery.valiadte.js
  • jquery.validate.unobtrusive

打開 Login.cshtml,在文件頂部包含這三個js文件:

   1:  <script src="~/Scripts/jquery-1.8.0.js"></script>
   2:  <script src="~/Scripts/jquery.validate.js"></script>
   3:  <script src="~/Scripts/jquery.validate.unobtrusive.js"></script>

3. 運行

關於實驗21

客戶端驗證是如何實現的?

如上所述,客戶端驗證並不是很麻煩,在Login View中,HTML元素能夠使用幫助類來生成,Helper 函數能夠根據Data Annotation屬性的使用生成帶有屬性的HTML 標記元素。

例如:

   1:  @Html.TextBoxFor(x=>x.UserName)
   2:  @Html.ValidationMessageFor(x=>x.UserName)

根據以上代碼生成的HTML 代碼如下:

   1:  <input data-val="true" data-val-length="UserName length should be between 2 and 7" data-val-length-max="7" data-val-length-min="2" id="UserName" name="UserName" type="text" value="" />
   2:  <span class="field-validation-error" data-valmsg-for="UserName" data-valmsg-replace="true"> </span>

jQuery Unobtrusive驗證文件會使用這些自定義的HTML 屬性,驗證會在客戶端自動生成。自動進行客戶端驗證是使用HTML 幫助類的又一大好處。

是否可以使用不帶HTML 幫助類的JavaScript  驗證?

是,可手動添加屬性。

總結

這就是本節所講的用戶授權與客戶端驗證的實現,在第五天我們會講到更高級的應用,請持續關注,不要走開哦!

有了本節MVC關於用戶授權與客戶端驗證的講解,相信會對大家的MVC開發過程有所幫助。在使用MVC進行開發時,還可以利用一些開發工具。使用 ComponentOne Studio ASP.NET MVC 這款輕量級控件,在開發效率大大提高的同時,工作量也會大大減少。

 

原文鏈接   http://www.codeproject.com/Articles/996832/Learn-MVC-Project-in-Days-Day

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章