七天学会ASP.NET MVC (四)——用户授权认证问题

小编应各位的要求,快马加鞭,马不停蹄的最终:七天学会 Asp.Net MVC 第四篇出炉。在第四天的学习中。我们主要了学习怎样在MVC中怎样实现认证授权等问题。本节主要讲了验证错误时的错误值,client验证,授权认证及登录注销功能的实现。

系列文章

七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC

七天学会ASP.NET MVC (二)——ASP.NET MVC 数据传递

七天学会ASP.NET MVC (三)——ASP.Net MVC 数据处理

七天学会ASP.NET MVC (四)——用户授权认证问题

七天学会ASP.NET MVC (五)——Layout页面使用和用户角色管理

day4

目录

实验15 ——有关错误验证的保留值。

实验16——加入client验证

实验17——加入授权认证

实验18——在View中显示username

实验19——实现注销操作

实验20——实现登录页面验证

实验21——实现登录页面client验证

总结

实验15——有关错误验证的保留值

在上一节的实验13。我们介绍了server端的身份验证,实验14中加入了client验证的支持,希望每位读者都能够把实验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”button。

会出现验证失败。可是数字 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——加入client验证

首先了解,须要验证什么?

1. FirstName 不能为空

2. LastName字符长度不能大于5

3. Salary不能为空,且应该为数字类型

4. FirstName 不能包括@字符

接下来,实现client验证功能

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 += "
" + FirstNameEmptyMessage;
  43:      if (FirstNameInValidMessage != "")
  44:          FinalErrorMessage += "
" + FirstNameInValidMessage;
  45:      if (LastNameInValidMessage != "")
  46:          FinalErrorMessage += "
" + LastNameInValidMessage;
  47:      if (SalaryEmptyMessage != "")
  48:          FinalErrorMessage += "
" + SalaryEmptyMessage;
  49:      if (SalaryInvalidMessage != "")
  50:          FinalErrorMessage += "
" + 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.  在点击 SaveEmployeebutton时,调用验证函数。例如以下:

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

5.  执行測试

点击 Add New 链接。跳转到 ”Add  New“页面

測试1

測试2

关于实验16

为什么在点击”SaveEmployee “button时,须要返回keyword?

如之前实验9讨论的,当点击提交button时,是给server发送请求,验证失败时对server请求没有意义。通过加入”return false“代码,能够取消默认的server请求。

在 IsValid函数将返回false。表示验证失败来实现预期的功能。

除了提示用户。能否够在当前页面显示错误信息?

是能够得。仅仅须要为每一个错误创建span 标签,默认设置为不可见,当提交button点击时,假设验证失败,使用JavaScript改动错误的可见性。

自己主动获取client验证还有什么方法?

是,当使用Html 帮助类,可依据服务端验证来获取自己主动client验证。在以后会具体讨论。

server端验证还有没有必须使用?

在一些JavaScript脚本代码无法使用时。server端能够替代使用。

实验 17 加入授权认证

在实验17中,会改进GetView方法,使其变得更加安全。仅仅有合法的用户才干够訪问该方法。

在本系列的第一讲中,我们了解了Asp.Net和MVC的意义,知道MVC是Asp.net的一部分,MVC继承了ASP.NET的全部特征,包括表单认证。

先来了解ASP.NET是怎样进行Form认证的。

  1. 终端用户在浏览器的帮助下,发送Form认证请求。
  2. 浏览器会发送存储在client的全部相关的用户数据。

  3. 当server端接收到请求时,server会检測请求,查看是否存在 “Authentication Cookie”的Cookie。
  4. 假设查找到认证Cookie,server会识别用户,验证用户是否合法。
  5. 假设为找到“Authentication Cookie”,server会将用户作为匿名(未认证)用户处理,在这样的情况下。假设请求的资源标记着 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是必须写的吗?

是必须写的。让我们了解一些小的工作细节。

  • client通过浏览器给server发送请求。

  • 当通过浏览器生成,全部相关的Cookies也会随着请求一起发送。
  • server接收请求后。准备响应。

  • 请求和响应都是通过HTTP协议传输的。HTTP是无状态协议。

    每一个请求都是新请求。因此当同一client发出二次请求时,server无法识别,为了解决此问题,server会在准备好的请求包中加入一个Cookie。然后返回。

  • 当client的浏览器接收到带有Cookie的响应,会在client创建Cookies。
  • 假设client再次给server发送请求,server就会识别。

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中显示已登录的username

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——登录页面实现client验证

在本实验中介绍一种方法实现client验证

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

client验证是怎样实现的?

如上所述,client验证并非非常麻烦,在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 属性,验证会在client自己主动生成。自己主动进行client验证是使用HTML 帮助类的又一大优点。

能否够使用不带HTML 帮助类的JavaScript  验证?

是,可手动加入属性。

总结

这就是本节所讲的用户授权与client验证的实现。在第五天我们会讲到更高级的应用,请持续关注,不要走开哦。

有了本节MVC关于用户授权与client验证的解说。相信会对大家的MVC开发过程有所帮助。在使用MVC进行开发时。还能够利用一些开发工具。

使用 ComponentOne Studio ASP.NET MVC 这款轻量级控件,在开发效率大大提高的同一时候,工作量也会大大降低。


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

原文地址:https://www.cnblogs.com/mthoutai/p/7071737.html