注:本文是【ASP.NET Identity系列教程】的第二篇。本系列教程詳細、完整、深刻地介紹了微軟的ASP.NET Identity技術,描述瞭如何運用ASP.NET Identity實現應用程序的用戶管理,以及實現應用程序的認證與受權等相關技術,譯者但願本系列教程能成爲掌握ASP.NET Identity技術的一份完整而有價值的資料。讀者如果可以按照文章的描述,一邊閱讀、一邊實踐、一邊理解,定能有意想不到的巨大收穫!但願本系列博文可以獲得廣大園友的高度推薦。html
In this chapter, I show you how to apply ASP.NET Identity to authenticate and authorize the user accounts created in the previous chapter. I explain how the ASP.NET platform provides a foundation for authenticating requests and how ASP.NET Identity fits into that foundation to authenticate users and enforce authorization through roles. Table 14-1 summarizes this chapter.
本章將演示如何將ASP.NET Identity用於對上一章中建立的用戶帳號進行認證與受權。我將解釋ASP.NET平臺對請求進行認證的基礎,並解釋ASP.NET Identity如何融入這種基礎對用戶進行認證,以及經過角色加強受權功能。表14-1描述了本章概要。web
Problem 問題 |
Solution 解決方案 |
Listing 清單號 |
---|---|---|
Prepare an application for user authentication. 準備用戶認證的應用程序 |
Apply the Authorize attribute to restrict access to action methods and define a controller to which users will be redirected to provide credentials. 運用Authorize註解屬性來限制對動做方法的訪問,並定義一個對用戶重定向的控制器,以便讓用戶提供憑據 |
1–4 |
Authenticate a user. 認證用戶 |
Check the name and password using the FindAsync method defined by the user manager class and create an implementation of the IIdentity interface using the CreateIdentityMethod. Set an authentication cookie for subsequent requests by calling the SignIn method defined by the authentication manager class. 使用由用戶管理器類定義的FindAsync方法檢查用戶名和口令,並使用CreateIdentityMethod建立一個IIdentity接口的實現。經過調用由認證管理器類定義的SignIn方法,設置後繼請求的認證Cookie。 |
5 |
Prepare an application for role-based authorization. 準備基於角色受權的應用程序 |
Create a role manager class and register it for instantiation in the OWIN startup class. 建立一個角色管理器類,將其註冊爲OWIN啓動類中的實例化 |
6–8 |
Create and delete roles. 建立和刪除角色 |
Use the CreateAsync and DeleteAsync methods defined by the role manager class. 使用由角色管理器類定義的CreateAsync和DeleteAsync方法。 |
9–12 |
Manage role membership. 管理角色成員 |
Use the AddToRoleAsync and RemoveFromRoleAsync methods defined by the user manager class. 使用由用戶管理器類定義的AddToRoleAsync和RemoveFromRoleAsync方法 |
13–15 |
Use roles for authorization. 使用角色進行受權 |
Set the Roles property of the Authorize attribute. 設置Authorize註解屬性的Roles屬性 |
16–19 |
Seed the database with initial content. 將初始化內容種植數據庫 |
Use the database context initialization class. 使用數據庫上下文的初始化類 |
20, 21 |
In this chapter, I am going to continue working on the Users project I created in Chapter 13. No changes to the application components are required.
在本章中,我打算繼續沿用第13章所建立的Users項目,不須要修改該應用程序的組件。數據庫
The most fundamental activity for ASP.NET Identity is to authenticate users, and in this section, I explain and demonstrate how this is done. Table 14-2 puts authentication into context.
ASP.NET Identity最基本的活動就是認證用戶,在本小節中,我將解釋並演示其作法。表14-2描述了認證的情形。數組
Question 問題 |
Answer 回答 |
---|---|
What is it? 什麼是認證? |
Authentication validates credentials provided by users. Once the user is authenticated, requests that originate from the browser contain a cookie that represents the user identity. 認證是驗證用戶提供的憑據。一旦用戶已被認證,源自該瀏覽器的請求便會含有表示該用戶標識的Cookie。 |
Why should I care? 爲什麼要關心它? |
Authentication is how you check the identity of your users and is the first step toward restricting access to sensitive parts of the application. 認證是你檢查用戶標識的辦法,也是限制對應用程序敏感部分進行訪問的第一步。 |
How is it used by the MVC framework? 如何在MVC框架中使用它? |
Authentication features are accessed through the Authorize attribute, which is applied to controllers and action methods in order to restrict access to authenticated users. 認證特性是經過Authorize註解屬性進行訪問的,將該註解屬性運用於控制器和動做方法,目的是將訪問限制到已認證用戶。 |
Tip I use names and passwords stored in the ASP.NET Identity database in this chapter. In Chapter 15, I demonstrate how ASP.NET Identity can be used to authenticate users with a service from Google (Identity also supports authentication for Microsoft, Facebook, and Twitter accounts).
提示:本章會使用存儲在ASP.NET Identity數據庫中的用戶名和口令。在第15章中將演示如何將ASP.NET Identity用於認證享有Google服務的用戶(Identity還支持對Microsoft、Facebook以及Twitter帳號的認證)。瀏覽器
The ASP.NET Identity system integrates into the ASP.NET platform, which means you use the standard MVC framework techniques to control access to action methods, such as the Authorize attribute. In this section, I am going to apply basic restrictions to the Index action method in the Home controller and then implement the features that allow users to identify themselves so they can gain access to it. Listing 14-1 shows how I have applied the Authorize attribute to the Home controller.
ASP.NET Identity系統集成到了ASP.NET平臺,這意味着你可使用標準的MVC框架技術來控制對動做方法的訪問,例如使用Authorize註解屬性。在本小節中,我打算在Home控制中的Index動做方法上運用基本的限制,而後實現讓用戶對本身進行標識,以使他們可以訪問。清單14-1演示瞭如何將Authorize註解屬性運用於Home控制器。安全
Listing 14-1. Securing the Home Controller
清單14-1. 實施Home控制器的安全服務器
using System.Web.Mvc; using System.Collections.Generic;
namespace Users.Controllers {
public class HomeController : Controller {
[Authorize] public ActionResult Index() { Dictionary<string, object> data = new Dictionary<string, object>(); data.Add("Placeholder", "Placeholder"); return View(data); } } }
Using the Authorize attribute in this way is the most general form of authorization and restricts access to the Index action methods to requests that are made by users who have been authenticated by the application.
這種方式使用Authorize註解屬性是受權的最通常形式,它限制了對Index動做方法的訪問,由用戶發送給該動做方法的請求必須是應用程序已認證的用戶。cookie
If you start the application and request a URL that targets the Index action on the Home controller (/Home/Index, /Home, or just /), you will see the error shown by Figure 14-1.
若是啓動應用程序,並請求以Home控制器中Index動做爲目標的URL(/Home/Index、/Home或/),將會看到如圖14-1所示的錯誤。session
Figure 14-1. Requesting a protected URL
圖14-1. 請求一個受保護的URL架構
The ASP.NET platform provides some useful information about the user through the HttpContext object, which is used by the Authorize attribute to check the status of the current request and see whether the user has been authenticated. The HttpContext.User property returns an implementation of the IPrincipal interface, which is defined in the System.Security.Principal namespace. The IPrincipal interface defines the property and method shown in Table 14-3.
ASP.NET平臺經過HttpContext對象提供一些關於用戶的有用信息,該對象由Authorize註解屬性使用的,以檢查當前請求的狀態,考察用戶是否已被認證。HttpContext.User屬性返回的是IPrincipal接口的實現,該接口是在System.Security.Principal命名空間中定義的。IPrincipal接口定義瞭如表14-3所示的屬性和方法。
Name 名稱 |
Description 描述 |
---|---|
Identity | Returns an implementation of the IIdentity interface that describes the user associated with the request. 返回IIdentity接口的實現,它描述了與請求相關聯的用戶 |
IsInRole(role) | Returns true if the user is a member of the specified role. See the 「Authorizing Users with Roles」 section for details of managing authorizations with roles. 若是用戶是指定角色的成員,則返回true。參見「以角色受權用戶」小節,其中描述了以角色進行受權管理的細節 |
The implementation of IIdentity interface returned by the IPrincipal.Identity property provides some basic, but useful, information about the current user through the properties I have described in Table 14-4.
由IPrincipal.Identity屬性返回的IIdentity接口實現經過一些屬性提供了有關當前用戶的一些基本卻有用的信息,表14-4描述了這些屬性。
Name 名稱 |
Description 描述 |
---|---|
AuthenticationType | Returns a string that describes the mechanism used to authenticate the user 返回一個字符串,描述了用於認證用戶的機制 |
IsAuthenticated | Returns true if the user has been authenticated 若是用戶已被認證,返回true。 |
Name | Returns the name of the current user 返回當前用戶的用戶名 |
Tip In Chapter 15 I describe the implementation class that ASP.NET Identity uses for the IIdentity interface, which is called ClaimsIdentity.
提示:第15章會描述ASP.NET Identity用於IIdentity接口的實現類,其名稱爲ClaimsIdentity。
ASP.NET Identity contains a module that handles the AuthenticateRequest life-cycle event, which I described in Chapter 3, and uses the cookies sent by the browser to establish whether the user has been authenticated. I’ll show you how these cookies are created shortly. If the user is authenticated, the ASP.NET framework module sets the value of the IIdentity.IsAuthenticated property to true and otherwise sets it to false. (I have yet to implement the feature that will allow users to authenticate, which means that the value of the IsAuthenticated property is always false in the example application.)
ASP.NET Identity含有一個處理AuthenticateRequest生命週期事件(第3章曾作過描述)的模塊,並使用瀏覽器發送過來的Cookie確認用戶是否已被認證。我很快會演示如何建立這些Cookie。若是用戶已被認證,此ASP.NET框架模塊便會將IIdentity.IsAuthenticated屬性的值設置爲true,不然設置爲false。(此刻還沒有實現讓用戶進行認證的特性,這意味着在本示例應用程序中,IsAuthenticated屬性的值老是false。)
The Authorize module checks the value of the IsAuthenticated property and, finding that the user isn’t authenticated, sets the result status code to 401 and terminates the request. At this point, the ASP.NET Identity module intercepts the request and redirects the user to the /Account/Login URL. This is the URL that I defined in the IdentityConfig class, which I specified in Chapter 13 as the OWIN startup class, like this:
Authorize模塊檢查IsAuthenticated屬性的值,會發現該用戶是未認證的,因而將結果狀態碼設置爲401(未受權),並終止該請求。但在這一點處(這裏是ASP.NET Identity在請求生命週期中的切入點——譯者注),ASP.NET Identity模塊會攔截該請求,並將用戶重定向到/Account/Login URL。我在IdentityConfig類中已定義了此URL,IdentityConfig是第13章所指定的OWIN啓動類,以下所示:
using Microsoft.AspNet.Identity; using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; using Owin; using Users.Infrastructure;
namespace Users { public class IdentityConfig { public void Configuration(IAppBuilder app) {
app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), }); } } }
The browser requests the /Account/Login URL, but since it doesn’t correspond to any controller or action in the example project, the server returns a 404 – Not Found response, leading to the error message shown in Figure 14-1.
瀏覽器請求/Account/Login時,但由於示例項目中沒有相應的控制器或動做,於服務器返回了「404 – 未找到」響應,從而致使瞭如圖14-1所示的錯誤消息。
Even though the request ends in an error message, the request in the previous section illustrates how the ASP.NET Identity system fits into the standard ASP.NET request life cycle. The next step is to implement a controller that will receive requests for the /Account/Login URL and authenticate the user. I started by adding a new model class to the UserViewModels.cs file, as shown in Listing 14-2.
雖然請求終止於一條錯誤消息,但上一小節的請求已勾畫出ASP.NET Identity系統是如何切入標準的ASP.NET請求生命週期的。下一個步驟是實現一個控制器,用它來接收對/Account/Login URL的請求,並認證用戶。我首先在UserViewModels.cs文件中添加了一個模型類,如清單14-2所示。
Listing 14-2. Adding a New Model Class to the UserViewModels.cs File
清單14-2. 在UserViewModels.cs文件中添加一個新的模型類
using System.ComponentModel.DataAnnotations;
namespace Users.Models {
public class CreateModel { [Required] public string Name { get; set; } [Required] public string Email { get; set; } [Required] public string Password { get; set; } }
public class LoginModel { [Required] public string Name { get; set; } [Required] public string Password { get; set; } } }
The new model has Name and Password properties, both of which are decorated with the Required attribute so that I can use model validation to check that the user has provided values.
新模型具備Name和Password屬性,二者都用Required註解屬性進行了註釋,以使我可以使用模型驗證來檢查用戶是否提供了這些屬性的值。
Tip In a real project, I would use client-side validation to check that the user has provided name and password values before submitting the form to the server, but I am going to keep things focused on identity and the server-side functionality in this chapter. See Pro ASP.NET MVC 5 for details of client-side form validation.
提示:在一個實際的項目中,我會在用戶將表單遞交到服務器以前,使用客戶端驗證來檢查用戶已經提供了用戶名和口令的值,但在本章中,我打算把注意力集中在標識和服務器端的功能方面。客戶端表單驗證的詳情可參閱Pro ASP.NET MVC 5一書。
I added an Account controller to the project, as shown in Listing 14-3, with Login action methods to collect and process the user’s credentials. I have not implemented the authentication logic in the listing because I am going to define the view and then walk through the process of validating user credentials and signing users into the application.
我在項目中添加了一個Account控制器,如清單14-3所示,其中帶有Login動做方法,用以收集和處理用戶的憑據。該清單還沒有實現認證邏輯,由於我打算先定義視圖,而後再實現驗證用戶憑據的過程,並讓用戶簽入應用程序。
Listing 14-3. The Contents of the AccountController.cs File
清單14-3. AccountController.cs文件的內容
using System.Threading.Tasks; using System.Web.Mvc; using Users.Models;
namespace Users.Controllers {
[Authorize] public class AccountController : Controller {
[AllowAnonymous] public ActionResult Login(string returnUrl) { if (ModelState.IsValid) { } ViewBag.returnUrl = returnUrl; return View(); }
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginModel details, string returnUrl) { return View(details); } } }
Even though it doesn’t authenticate users yet, the Account controller contains some useful infrastructure that I want to explain separately from the ASP.NET Identity code that I’ll add to the Login action method shortly.
儘管它此刻還沒有認證用戶,但Account控制器已包含了一些有用的基礎結構,我想經過ASP.NET Identity代碼對這些結構分別加以解釋,很快就會在Login動做方法中添加這些代碼。
First, notice that both versions of the Login action method take an argument called returnUrl. When a user requests a restricted URL, they are redirected to the /Account/Login URL with a query string that specifies the URL that the user should be sent back to once they have been authenticated. You can see this if you start the application and request the /Home/Index URL. Your browser will be redirected, like this:
首先要注意Login動做方法有兩個版本,它們都有一個名稱爲returnUrl的參數。當用戶請求一個受限的URL時,他們被重定向到/Account/Login URL上,並帶有查詢字符串,該字符串指定了一旦用戶獲得認證後將用戶返回的URL,以下所示:
/Account/Login?ReturnUrl=%2FHome%2FIndex
The value of the ReturnUrl query string parameter allows me to redirect the user so that navigating between open and secured parts of the application is a smooth and seamless process.
ReturnUrl查詢字符串參數的值可以讓我可以對用戶進行重定向,使應用程序公開和保密部分之間的導航成爲一個平滑無縫的過程。
Next, notice the attributes that I have applied to the Account controller. Controllers that manage user accounts contain functionality that should be available only to authenticated users, such as password reset, for example. To that end, I have applied the Authorize attribute to the controller class and then used the AllowAnonymous attribute on the individual action methods. This restricts action methods to authenticated users by default but allows unauthenticated users to log in to the application.
下一個要注意的是運用於Account控制器的註解屬性。管理用戶帳號的控制器含有隻能由已認證用戶才能使用的功能,例如口令重置。爲此,我在控制器類上運用了Authorize註解屬性,而後又在個別動做方法上運用了AllowAnonymous註解屬性。這會將這些動做方法默認限制到已認證用戶,但又能容許未認證用戶登陸到應用程序。
Finally, I have applied the ValidateAntiForgeryToken attribute, which works in conjunction with the Html.AntiForgeryToken helper method in the view and guards against cross-site request forgery. Cross-site forgery exploits the trust that your user has for your application and it is especially important to use the helper and attribute for authentication requests.
最後要注意的是,我運用了ValidateAntiForgeryToken註解屬性,該屬性與視圖中的Html.AntiForgeryToken輔助器方法聯合工做,防止Cross-Site Request Forgery(CSRF,跨網站請求僞造)的攻擊。CSRF會利用應用程序對用戶的信任,所以使用這個輔助器和註解屬性對於認證請求是特別重要的。
Tip you can learn more about cross-site request forgery at http://en.wikipedia.org/wiki/Cross-site_request_forgery.
提示:更多關於CSRF的信息,請參閱http://en.wikipedia.org/wiki/Cross-site_request_forgery。
My last preparatory step is to create the view that will be rendered to gather credentials from the user. Listing 14-4 shows the contents of the Views/Account/Login.cshtml file, which I created by right-clicking the Index action method and selecting Add View from the pop-up menu.
最後一項準備步驟是建立一個視圖,用以收集來自於用戶的憑據。清單14-4顯示了Views/Account/Login.cshtml文件的內容,這是經過右擊Index動做方法,而後從彈出菜單選擇「Add View(添加視圖)」而建立的。
Listing 14-4. The Contents of the Login.cshtml File
清單14-4. Login.cshtml文件的內容
@model Users.Models.LoginModel @{ ViewBag.Title = "Login";} <h2>Log In</h2>
@Html.ValidationSummary()
@using (Html.BeginForm()) { @Html.AntiForgeryToken(); <input type="hidden" name="returnUrl" value="@ViewBag.returnUrl" /> <div class="form-group"> <label>Name</label> @Html.TextBoxFor(x => x.Name, new { @class = "form-control" }) </div> <div class="form-group"> <label>Password</label> @Html.PasswordFor(x => x.Password, new { @class = "form-control" }) </div> <button class="btn btn-primary" type="submit">Log In</button> }
The only notable aspects of this view are using the Html.AntiForgeryToken helper and creating a hidden input element to preserve the returnUrl argument. In all other respects, this is a standard Razor view, but it completes the preparations for authentication and demonstrates the way that unauthenticated requests are intercepted and redirected. To test the new controller, start the application and request the /Home/Index URL. You will be redirected to the /Account/Login URL, as shown in Figure 14-2.
該視圖惟一要注意的方面是使用了Html.AntiForgeryToken輔助器,並建立了一個隱藏的input元素,以保護returnUrl參數。在其餘方面,這是一個標準的Razor視圖,但它實現了認證的準備工做,並能演示被攔截且被重定向的未認證請求的狀況。爲了測試這個新的控制器,啓動應用程序,並請求/Home/Index。你將被重定向到/Account/Login,如圖14-2所示。
Figure 14-2. Prompting the user for authentication credentials
圖14-2. 提示用戶輸入認證憑據
Requests for protected URLs are being correctly redirected to the Account controller, but the credentials provided by the user are not yet used for authentication. In Listing 14-5, you can see how I have completed the implementation of the Login action.
發送給受保護URL的請求會被正確地重定向到Account控制器,但由用戶提供的憑據還沒有被用於認證。從清單14-5能夠看出如何完成Login動做的實現。
Listing 14-5. Adding Authentication to the AccountController.cs File
清單14-5. 在AccountController.cs文件中添加認證
using System.Threading.Tasks; using System.Web.Mvc; using Users.Models; using Microsoft.Owin.Security; using System.Security.Claims; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Users.Infrastructure; using System.Web;
namespace Users.Controllers {
[Authorize] public class AccountController : Controller {
[AllowAnonymous] public ActionResult Login(string returnUrl) { ViewBag.returnUrl = returnUrl; return View(); }
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginModel details, string returnUrl) { if (ModelState.IsValid) { AppUser user = await UserManager.FindAsync(details.Name, details.Password); if (user == null) { ModelState.AddModelError("", "Invalid name or password."); } else { ClaimsIdentity ident = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); AuthManager.SignOut(); AuthManager.SignIn(new AuthenticationProperties { IsPersistent = false}, ident); return Redirect(returnUrl); } } ViewBag.returnUrl = returnUrl; return View(details); }
private IAuthenticationManager AuthManager { get { return HttpContext.GetOwinContext().Authentication; } } private AppUserManager UserManager { get { return HttpContext.GetOwinContext().GetUserManager<AppUserManager>(); } } } }
The simplest part is checking the credentials, which I do through the FindAsync method of the AppUserManager class, which you will remember as the user manager class from Chapter 13:
最簡單的部分是檢查憑據,這是經過AppUserManager類的FindAsync方法來作的,你可能還記得,AppUserManager是第13章的用戶管理器類。
... AppUser user = await UserManager.FindAsync(details.Name, details.Password); ...
I will be using the AppUserManager class repeatedly in the Account controller, so I defined a property called UserManager that returns the instance of the class using the GetOwinContext extension method for the HttpContext class, just as I did for the Admin controller in Chapter 13.
我會在Account控制器中反覆使用AppUserManager類,所以定義了一個名稱爲的UserManager屬性,它使用HttpContext類的GetOwinContext擴展方法來返回AppUserManager類的實例。
The FindAsync method takes the account name and password supplied by the user and returns an instance of the user class (AppUser in the example application) if the user account exists and if the password is correct. If there is no such account or the password doesn’t match the one stored in the database, then the FindAsync method returns null, in which case I add an error to the model state that tells the user that something went wrong.
FindAsync方法以用戶提供的帳號名和口令爲參數,並在該用戶帳號存在且口令正確時,返回一個用戶類(此例中的AppUser)的實例。若是無此帳號,或者與數據庫存儲的不匹配,那麼FindAsync方法返回空(null),出現這種狀況時,我給模型狀態添加了一條錯誤消息,告訴用戶可能出錯了。
If the FindAsync method does return an AppUser object, then I need to create the cookie that the browser will send in subsequent requests to show they are authenticated. Here are the relevant statements:
若是FindAsync方法確實返回了AppUser對象,那麼則須要建立Cookie,瀏覽器會在後繼的請求中發送這個Cookie,代表他們是已認證的。如下是有關語句:
... ClaimsIdentity ident = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); AuthManager.SignOut(); AuthManager.SignIn(new AuthenticationProperties {IsPersistent = false}, ident); return Redirect(returnUrl); ...
The first step is to create a ClaimsIdentity object that identifies the user. The ClaimsIdentity class is the ASP.NET Identity implementation of the IIdentity interface that I described in Table 14-4 and that you can see used in the 「Using Roles for Authorization」 section later in this chapter.
第一步是建立一個標識該用戶的ClaimsIdentity對象。ClaimsIdentity類是表14-4所描述的IIdentity接口的ASP.NET Identity實現,能夠在本章稍後的「使用角色受權」小節中看到它的使用。
Tip Don’t worry about why the class is called ClaimsIdentity at the moment. I explain what claims are and how they can be used in Chapter 15.
提示:此刻沒必要關心這個類爲何要調用ClaimsIdentity,第15章會解釋什麼是聲明(Claims),並介紹如何使用它們。
Instances of ClaimsIdentity are created by calling the user manager CreateIdentityAsync method, passing in a user object and a value from the DefaultAuthenticationTypes enumeration. The ApplicationCookie value is used when working with individual user accounts.
ClaimsIdentity的實例是調用用戶管理器的CreateIdentityAsync方法而建立的,在其中傳遞了一個用戶對象和DefaultAuthenticationTypes枚舉中的一個值。在使用個別用戶帳號進行工做時,會用到ApplicationCookie值。
The next step is to invalidate any existing authentication cookies and create the new one. I defined the AuthManager property in the controller because I’ll need access to the object it provides repeatedly as I build the functionality in this chapter. The property returns an implementation of the IAuthenticationManager interface that is responsible for performing common authentication options. I have described the most useful methods provided by the IAuthenticationManager interface in Table 14-5.
下一個步驟是讓已認證的Cookie失效,並建立一個新的Cookie。我在該控制器中定義了AuthManager屬性,由於在創建本章功能過程當中,須要反覆訪問它所提供的對象。該屬性返回的是IAuthenticationManager接口的實現,它負責執行常規的認證選項。表14-5中描述了IAuthenticationManager接口所提供的最有用的方法。
Name 名稱 |
Description 描述 |
---|---|
SignIn(options, identity) | Signs the user in, which generally means creating the cookie that identifies authenticated requests 簽入用戶,這一般意味着要建立用來標識已認證請求的Cookie |
SignOut() | Signs the user out, which generally means invalidating the cookie that identifies authenticated requests 簽出用戶,這一般意味着使標識已認證用戶的Cookie失效 |
The arguments to the SignIn method are an AuthenticationProperties object that configures the authentication process and the ClaimsIdentity object. I set the IsPersistent property defined by the AuthenticationProperties object to true to make the authentication cookie persistent at the browser, meaning that the user doesn’t have to authenticate again when starting a new session. (There are other properties defined by the AuthenticationProperties class, but the IsPersistent property is the only one that is widely used at the moment.)
SignIn方法的參數是一個AuthenticationProperties對象,用以配置認證過程以及ClaimsIdentity對象。我將AuthenticationProperties對象定義的IsPersistent屬性設置爲true,以使認證Cookie在瀏覽器中是持久化的,意即用戶在開始新會話時,沒必要再次進行認證。(AuthenticationProperties類還定義了一些其餘屬性,但IsPersistent屬性是此刻惟一要普遍使用的一個屬性。)
The final step is to redirect the user to the URL they requested before the authentication process started, which I do by calling the Redirect method.
最後一步是將用戶重定向到他們在認證過程開始以前所請求的URL,這是經過調用Redirect方法實現的。
CONSIDERING TWO-FACTOR AUTHENTICATION
考慮雙因子認證
I have performed single-factor authentication in this chapter, which is where the user is able to authenticate using a single piece of information known to them in advance: the password.
在本章中,我實行的是單因子認證,在這種場合中,用戶只需使用一個他們預知的單一信息片斷:口令,便可以進行認證。
ASP.NET Identity also supports two-factor authentication, where the user needs something extra, usually something that is given to the user at the moment they want to authenticate. The most common examples are a value from a SecureID token or an authentication code that is sent as an e-mail or text message (strictly speaking, the two factors can be anything, including fingerprints, iris scans, and voice recognition, although these are options that are rarely required for most web applications).
ASP.NET Identity還支持雙因子認證,在這種狀況下,用戶須要一些附加信息,一般是在他們須要認證時才發給他們的某種信息。最經常使用的例子是SecureID令牌的值,或者是經過E-mail發送的認證碼或文本消息(嚴格地講,第二因子能夠是任何東西,包括指紋、眼瞳掃描、聲音識別等,儘管這些是在大多數Web應用程序中不多須要用到的選項。)
Security is increased because an attacker needs to know the user’s password and have access to whatever provides the second factor, such an e-mail account or cell phone.
這樣增長了安全性,由於攻擊者須要知道用戶的口令,而且可以對提供第二因子的客戶端進行訪問,如E-mail帳號或移動電話等。
I don’t show two-factor authentication in the book for two reasons. The first is that it requires a lot of preparatory work, such as setting up the infrastructure that distributes the second-factor e-mails and texts and implementing the validation logic, all of which is beyond the scope of this book.
本章不演示雙因子認證有兩個緣由。第一是它須要許多準備工做,例如要創建分發第二因子的郵件和文本的基礎架構,並實現驗證邏輯,這些都超出了本書的範圍。
The second reason is that two-factor authentication forces the user to remember to jump through an additional hoop to authenticate, such as remembering their phone or keeping a security token nearby, something that isn’t always appropriate for web applications. I carried a SecureID token of one sort or another for more than a decade in various jobs, and I lost count of the number of times that I couldn’t log in to an employer’s system because I left the token at home.
第二個緣由是雙因子認證強制用戶要記住一個額外的認證令牌,例如,要記住他們的電話,或者將安全令牌帶在身邊,這對Web應用程序而言,並不是老是合適的。我十幾年在各類工做中都帶着這種或那種SecureID令牌,並且我有數不清的次數沒法登陸僱員系統,由於我將令牌丟在了家裏。
If you are interested in two-factor security, then I recommend relying on a third-party provider such as Google for authentication, which allows the user to choose whether they want the additional security (and inconvenience) that two-factor authentication provides. I demonstrate third-party authentication in Chapter 15.
若是對雙因子安全性有興趣,那麼我建議你依靠第三方提供器,例如Google認證,它容許用戶選擇是否但願使用雙因子提供的附加安全性(並且是不方便的)。第15章將演示第三方認證。
To test user authentication, start the application and request the /Home/Index URL. When redirected to the /Account/Login URL, enter the details of one of the users I listed at the start of the chapter (for instance, the name joe and the password MySecret). Click the Log In button, and your browser will be redirected back to the /Home/Index URL, but this time it will submit the authentication cookie that grants it access to the action method, as shown in Figure 14-3.
爲了測試用戶認證,啓動應用程序,並請求/Home/Index URL。當被重定向到/Account/Login URL時,輸入本章開始時列出的一個用戶的細節(例如,姓名爲joe,口令爲MySecret)。點擊「Log In(登陸)」按鈕,你的瀏覽器將被重定向,回到/Home/Index URL,但此次它將遞交認證Cookie,被准予訪問該動做方法,如圖14-3所示。
Figure 14-3. Authenticating a user
圖14-3. 認證用戶
Tip You can use the browser F12 tools to see the cookies that are used to identify authenticated requests.
提示:能夠用瀏覽器的F12工具,看到用來標識已認證請求的Cookie。
In the previous section, I applied the Authorize attribute in its most basic form, which allows any authenticated user to execute the action method. In this section, I will show you how to refine authorization to give finer-grained control over which users can perform which actions. Table 14-6 puts authorization in context.
上一小節以最基本的形式運用了Authorize註解屬性,這容許任何已認證用戶執行動做方法。在本小節中,將展現如何精煉受權,以便在用戶可以執行的動做上有更細粒度的控制。表14-6描述了受權的情形。
Question 問題 |
Answer 答案 |
---|---|
What is it? 什麼是受權? |
Authorization is the process of granting access to controllers and action methods to certain users, generally based on role membership. 受權是將控制器和動做的准許訪問限制到特定用戶,一般是基於角色的成員 |
Why should I care? 爲什麼要關心它? |
Without roles, you can differentiate only between users who are authenticated and those who are not. Most applications will have different types of users, such as customers and administrators. 沒有角色,你只能在已認證用戶和未認證用戶之間加以區分。大多數應用程序均有不一樣類型的用戶,例如客戶和管理員等 |
How is it used by the MVC framework? 如何在MVC框架中使用它? |
Roles are used to enforce authorization through the Authorize attribute, which is applied to controllers and action methods. 角色經過Authorize註解屬性可用於強制受權,Authorize可用於控制器和動做方法 |
Tip In Chapter 15, I show you a different approach to authorization using claims, which are an advanced ASP.NET Identity feature.
提示:第15章將使用Claims(聲明)來演示不一樣的受權辦法,Claims是一種高級的ASP.NET Identity特性。
ASP.NET Identity provides a strongly typed base class for accessing and managing roles called RoleManager<T> , where T is the implementation of the IRole interface supported by the storage mechanism used to represent roles. The Entity Framework uses a class called IdentityRole to implement the IRole interface, which defines the properties shown in Table 14-7.
ASP.NET Identity爲訪問和管理角色提供了一個強類型的基類,叫作RoleManager<T> ,其中T是IRole接口的實現,該實現獲得了用來表示角色的存儲機制的支持。Entity Framework實現了IRole接口,使用的是一個名稱爲IdentityRole的類,它定義瞭如表14-7所示的屬性。
Name 名稱 |
Description 描述 |
---|---|
Id | Defines the unique identifier for the role 定義角色的惟一標識符 |
Name | Defines the name of the role 定義角色名稱 |
Users | Returns a collection of IdentityUserRole objects that represents the members of the role 返回一個表明角色成員的IdentityUserRole對象集合 |
I don’t want to leak references to the IdentityRole class throughout my application because it ties me to the Entity Framework for storing role data, so I start by creating an application-specific role class that is derived from IdentityRole. I added a class file called AppRole.cs to the Models folder and used it to define the class shown in Listing 14-6.
我不但願在整個應用程序中都暴露對IdentityRole類的引用,由於它爲了存儲角色數據,將我綁定到了Entity Framework。爲此,我首先建立了一個應用程序專用的角色類,它派生於IdentityRole。我在Models文件夾中添加了一個類文件,名稱爲AppRole.cs,並用它定義了這個類,如清單14-6所示。
Listing 14-6. The Contents of the AppRole.cs File
清單14-6. AppRole文件的內容
using Microsoft.AspNet.Identity.EntityFramework;
namespace Users.Models { public class AppRole : IdentityRole {
public AppRole() : base() {}
public AppRole(string name) : base(name) { } } }
The RoleManager<T> class operates on instances of the IRole implementation class through the methods and properties shown in Table 14-8.
RoleManager<T> 類經過表14-8所示的方法和屬性對IRole實現類的實例進行操做。
Name 名稱 |
Description 描述 |
---|---|
CreateAsync(role) | Creates a new role 建立一個新角色 |
DeleteAsync(role) | Deletes the specified role 刪除指定角色 |
FindByIdAsync(id) | Finds a role by its ID 找到指定ID的角色 |
FindByNameAsync(name) | Finds a role by its name 找到指定名稱的角色 |
RoleExistsAsync(name) | Returns true if a role with the specified name exists 若是存在指定名稱的角色,返回true |
UpdateAsync(role) | Stores changes to the specified role 將修改存儲到指定角色 |
Roles | Returns an enumeration of the roles that have been defined 返回已被定義的角色枚舉 |
These methods follow the same basic pattern of theUserManager<T> class that I described in Chapter 13. Following the pattern I used for managing users, I added a class file called AppRoleManager.cs to the Infrastructure folder and used it to define the class shown in Listing 14-7.
這些方法與第13章描述的UserManager<T> 類有一樣的基本模式。按照對管理用戶所採用的模式,我在Infrastructure文件夾中添加了一個類文件,名稱爲AppRoleManager.cs,用它定義瞭如清單14-7所示的類。
Listing 14-7. The Contents of the AppRoleManager.cs File
清單14-7. AppRoleManager.cs文件的內容
using System; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; using Users.Models;
namespace Users.Infrastructure {
public class AppRoleManager : RoleManager<AppRole>, IDisposable {
public AppRoleManager(RoleStore<AppRole> store) : base(store) { }
public static AppRoleManager Create( IdentityFactoryOptions<AppRoleManager> options, IOwinContext context) { return new AppRoleManager(new RoleStore<AppRole>(context.Get<AppIdentityDbContext>())); } } }
This class defines a Create method that will allow the OWIN start class to create instances for each request where Identity data is accessed, which means I don’t have to disseminate details of how role data is stored throughout the application. I can just obtain and operate on instances of the AppRoleManager class. You can see how I have registered the role manager class with the OWIN start class, IdentityConfig, in Listing 14-8. This ensures that instances of the AppRoleManager class are created using the same Entity Framework database context that is used for the AppUserManager class.
這個類定義了一個Create方法,它讓OWIN啓動類可以爲每個訪問Identity數據的請求建立實例,這意味着在整個應用程序中,我沒必要散佈如何存儲角色數據的細節,卻能獲取AppRoleManager類的實例,並對其進行操做。在清單14-8中能夠看到如何用OWIN啓動類(IdentityConfig)來註冊角色管理器類。這樣可以確保,可使用與AppUserManager類所用的同一個Entity Framework數據庫上下文,來建立AppRoleManager類的實例。
Listing 14-8. Creating Instances of the AppRoleManager Class in the IdentityConfig.cs File
清單14-8. 在IdentityConfig.cs文件中建立AppRoleManager類的實例
using Microsoft.AspNet.Identity; using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; using Owin; using Users.Infrastructure;
namespace Users { public class IdentityConfig { public void Configuration(IAppBuilder app) {
app.CreatePerOwinContext<AppIdentityDbContext>(AppIdentityDbContext.Create); app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create); app.CreatePerOwinContext<AppRoleManager>(AppRoleManager.Create); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), }); } } }
Having prepared the application for working with roles, I am going to create an administration tool for managing them. I will start the basics and define action methods and views that allow roles to be created and deleted. I added a controller called RoleAdmin to the project, which you can see in Listing 14-9.
如今已經作好了應用程序使用角色的準備,我打算建立一個管理工具來管理角色。首先從基本的開始,定義可以建立和刪除角色的動做方法和視圖。我在項目中添加了一個控制器,名稱爲RoleAdmin,如清單14-9所示。
Listing 14-9. The Contents of the RoleAdminController.cs File
清單14-9. RoleAdminController.cs文件的內容
using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using System.Web; using System.Web.Mvc; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Users.Infrastructure; using Users.Models;
namespace Users.Controllers { public class RoleAdminController : Controller {
public ActionResult Index() { return View(RoleManager.Roles); }
public ActionResult Create() { return View(); }
[HttpPost] public async Task<ActionResult> Create([Required]string name) { if (ModelState.IsValid) { IdentityResult result = await RoleManager.CreateAsync(new AppRole(name)); if (result.Succeeded) { return RedirectToAction("Index"); } else { AddErrorsFromResult(result); } } return View(name); }
[HttpPost] public async Task<ActionResult> Delete(string id) { AppRole role = await RoleManager.FindByIdAsync(id); if (role != null) { IdentityResult result = await RoleManager.DeleteAsync(role); if (result.Succeeded) { return RedirectToAction("Index"); } else { return View("Error", result.Errors); } } else { return View("Error", new string[] { "Role Not Found" }); } }
private void AddErrorsFromResult(IdentityResult result) { foreach (string error in result.Errors) { ModelState.AddModelError("", error); } }
private AppUserManager UserManager { get { return HttpContext.GetOwinContext().GetUserManager<AppUserManager>(); } }
private AppRoleManager RoleManager { get { return HttpContext.GetOwinContext().GetUserManager<AppRoleManager>(); } } } }
I have applied many of the same techniques that I used in the Admin controller in Chapter 13, including a UserManager property that obtains an instance of the AppUserManager class and an AddErrorsFromResult method that processes the errors reported in an IdentityResult object and adds them to the model state.
這裏運用了許多第13章中Admin控制器所採用的一樣技術,包括一個UserManager屬性,用於獲取AppUserManager類的實例;和一個AddErrorsFromResult方法,用來處理IdentityResult對象所報告的消息,並將消息添加到模型狀態。
I have also defined a RoleManager property that obtains an instance of the AppRoleManager class, which I used in the action methods to obtain and manipulate the roles in the application. I am not going to describe the action methods in detail because they follow the same pattern I used in Chapter 13, using the AppRoleManager class in place of AppUserManager and calling the methods I described in Table 14-8.
我還定義了RoleManager屬性,用來獲取AppRoleManager類的實例,在動做方法中用該實例獲取並維護應用程序的角色。我不打算詳細描述這些動做方法,由於它們遵循着與第13章一樣的模式,在使用AppUserManager的地方使用了AppRoleManager類,調用的是表14-8中的方法。
The views for the RoleAdmin controller are standard HTML and Razor markup, but I have included them in this chapter so that you can re-create the example. I want to display the names of the users who are members of each role. The Entity Framework IdentityRole class defines a Users property that returns a collection of IdentityUserRole user objects representing the members of the role. Each IdentityUserRole object has a UserId property that returns the unique ID of a user, and I want to get the username for each ID. I added a class file called IdentityHelpers.cs to the Infrastructure folder and used it to define the class shown in Listing 14-10.
RoleAdmin控制器的視圖是標準的HTML和Razor標記,但我仍是將它們包含在本章之中,以便你可以重建本章的示例。我但願顯示每一個角色中成員的用戶名。Entity Framework的IdentityRole類中定義了一個Users屬性,它可以返回表示角色成員的IdentityUserRole用戶對象集合。每個IdentityUserRole對象都有一個UserId屬性,它返回一個用戶的惟一ID,不過,我但願獲得的是每一個ID所對應的用戶名。我在Infrastructure文件夾中添加了一個類文件,名稱爲IdentityHelpers.cs,用它定義瞭如清單14-10所示的類。
Listing 14-10. The Contents of the IdentityHelpers.cs File
清單14-10. IdentityHelpers.cs文件的內容
using System.Web; using System.Web.Mvc; using Microsoft.AspNet.Identity.Owin;
namespace Users.Infrastructure { public static class IdentityHelpers {
public static MvcHtmlString GetUserName(this HtmlHelper html, string id) { AppUserManager mgr = HttpContext.Current.GetOwinContext().GetUserManager<AppUserManager>(); return new MvcHtmlString(mgr.FindByIdAsync(id).Result.UserName); } } }
Custom HTML helper methods are defined as extensions on the HtmlHelper class. My helper, which is called GetUsername, takes a string argument containing a user ID, obtains an instance of the AppUserManager through the GetOwinContext.GetUserManager method (where GetOwinContext is an extension method on the HttpContext class), and uses the FindByIdAsync method to locate the AppUser instance associated with the ID and to return the value of the UserName property.
這個自定義的HTML輔助器方法,是做爲HtmlHelper類的擴展進行定義的。該輔助器的名稱爲GetUsername,以一個含有用戶ID的字符串爲參數,經過GetOwinContext.GetUserManager方法獲取AppUserManager的一個實例(其中GetOwinContext是HttpContext類的擴展方法),並使用FindByIdAsync方法定位與ID相關聯的AppUser實例,而後返回UserName屬性的值。
Listing 14-11 shows the contents of the Index.cshtml file from the Views/RoleAdmin folder, which I created by right-clicking the Index action method in the code editor and selecting Add View from the pop-up menu.
清單14-11顯示了Views/RoleAdmin文件夾中Index.cshtml文件的內容,這是經過在代碼編輯器中右擊Index動做,並從彈出菜單中選擇「Add View(添加視圖)」來建立的。
Listing 14-11. The Contents of the Index.cshtml File in the Views/RoleAdmin Folder
清單14-11. Views/RoleAdmin文件夾中Index.cshtml文件的內容
@using Users.Models @using Users.Infrastructure @model IEnumerable<AppRole> @{ ViewBag.Title = "Roles"; }
<div class="panel panel-primary"> <div class="panel-heading">Roles</div> <table class="table table-striped">
<tr><th>ID</th><th>Name</th><th>Users</th><th></th></tr> @if (Model.Count() == 0) { <tr><td colspan="4" class="text-center">No Roles</td></tr> } else { foreach (AppRole role in Model) { <tr> <td>@role.Id</td> <td>@role.Name</td> <td> @if (role.Users == null || role.Users.Count == 0) { @: No Users in Role } else { <p>@string.Join(", ", role.Users.Select(x => Html.GetUserName(x.UserId)))</p> } </td> <td> @using (Html.BeginForm("Delete", "RoleAdmin", new { id = role.Id })) { @Html.ActionLink("Edit", "Edit", new { id = role.Id }, new { @class = "btn btn-primary btn-xs" }) <button class="btn btn-danger btn-xs" type="submit"> Delete </button> } </td> </tr> } } </table> </div> @Html.ActionLink("Create", "Create", null, new { @class = "btn btn-primary" })
This view displays a list of the roles defined by the application, along with the users who are members, and I use the GetUserName helper method to get the name for each user.
該視圖顯示了一個由應用程序定義的角色列表,且帶有成員用戶,我用GetUserName輔助器方法獲取了每一個用戶的用戶名。
Listing 14-12 shows the Views/RoleAdmin/Create.cshtml file, which I created to allow new roles to be created.
清單14-12顯示了Views/RoleAdmin/Create.cshtml文件,這是用來建立新角色的視圖。
Listing 14-12. The Contents of the Create.cshtml File in the Views/RoleAdmin Folder
清單14-12. Views/RoleAdmin文件夾中Create.cshtml文件的內容
@model string @{ ViewBag.Title = "Create Role";} <h2>Create Role</h2> @Html.ValidationSummary(false) @using (Html.BeginForm()) { <div class="form-group"> <label>Name</label> <input name="name" value="@Model" class="form-control" /> </div> <button type="submit" class="btn btn-primary">Create</button> @Html.ActionLink("Cancel", "Index", null, new { @class = "btn btn-default" }) }
The only information required to create a new view is a name, which I gather using a standard input element and submit the value to the Create action method.
建立該視圖須要的惟一信息是角色名,我用標準的input元素進行採集,並將該值遞交給Create動做方法。
To test the new controller, start the application and navigate to the /RoleAdmin/Index URL. To create a new role, click the Create button, enter a name in the input element, and click the second Create button. The new view will be saved to the database and displayed when the browser is redirected to the Index action, as shown in Figure 14-4. You can remove the role from the application by clicking the Delete button.
爲了測試新的控制器,啓動應用程序並導航到/RoleAdmin/Index URL。爲了建立一個新的角色,點擊「Create」按鈕,在input元素中輸入一個角色名,而後點擊第二個「Create」按鈕。新角色將被保存到數據庫,並在瀏覽器被重定向到Index動做時顯示出來,如圖14-4所示。能夠點擊「Delete」按鈕將該角色從應用程序中刪除。
Figure 14-4. Creating a new role
圖14-4. 建立新角色
To authorize users, it isn’t enough to just create and delete roles; I also have to be able to manage role memberships, assigning and removing users from the roles that the application defines. This isn’t a complicated process, but it invokes taking the role data from the AppRoleManager class and then calling the methods defined by the AppUserMangager class that associate users with roles.
爲了受權用戶,僅僅建立和刪除角色還不夠。還必須可以管理角色成員,從應用程序定義的角色中指定和除去用戶。這不是一個複雜的過程,但它要從AppRoleManager類獲取角色數據,而後調用將用戶與角色關聯在一塊兒的AppUserMangager類所定義的方法。
I started by defining view models that will let me represent the membership of a role and receive a new set of membership instructions from the user. Listing 14-13 shows the additions I made to the UserViewModels.cs file.
我首先定義了視圖模型,這讓我可以表示一個角色中的成員,並可以從用戶那裏接收一組新成員的指令。清單14-13顯示了在UserViewModels.cs文件中所作的添加。
Listing 14-13. Adding View Models to the UserViewModels.cs File
清單14-13. 添加到UserViewModels.cs文件的視圖模型
using System.ComponentModel.DataAnnotations; using System.Collections.Generic;
namespace Users.Models {
public class CreateModel { [Required] public string Name { get; set; } [Required] public string Email { get; set; } [Required] public string Password { get; set; } }
public class LoginModel { [Required] public string Name { get; set; } [Required] public string Password { get; set; } }
public class RoleEditModel { public AppRole Role { get; set; } public IEnumerable<AppUser> Members { get; set; } public IEnumerable<AppUser> NonMembers { get; set; } }
public class RoleModificationModel { [Required] public string RoleName { get; set; } public string[] IdsToAdd { get; set; } public string[] IdsToDelete { get; set; } } }
The RoleEditModel class will let me pass details of a role and details of the users in the system, categorized by membership. I use AppUser objects in the view model so that I can extract the name and ID for each user in the view that will allow memberships to be edited. The RoleModificationModel class is the one that I will receive from the model binding system when the user submits their changes. It contains arrays of user IDs rather than AppUser objects, which is what I need to change role memberships.
RoleEditModel類使我可以在系統中傳遞角色細節和用戶細節,按成員進行歸類。我在視圖模型中使用了AppUser對象,以使我在編輯成員的視圖中可以爲每一個用戶提取用戶名和ID。RoleModificationModel類是在用戶遞交他們的修改時,從模型綁定系統接收到的一個類。它含有用戶ID的數組,而不是AppUser對象,這是對角色成員進行修改所須要的。
Having defined the view models, I can add the action methods to the controller that will allow role memberships to be defined. Listing 14-14 shows the changes I made to the RoleAdmin controller.
定義了視圖模型以後,即可以在控制器中添加動做方法,以便定義角色成員。清單14-14顯示了我對RoleAdmin控制器所作的修改。
Listing 14-14. Adding Action Methods in the RoleAdminController.cs File
清單14-14. 在RoleAdminController.cs文件中添加動做方法
using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using System.Web; using System.Web.Mvc; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Users.Infrastructure; using Users.Models; using System.Collections.Generic;
namespace Users.Controllers { public class RoleAdminController : Controller {
// ...other action methods omitted for brevity... // ...出於簡化,這裏忽略了其餘動做方法 ...
public async Task<ActionResult> Edit(string id) { AppRole role = await RoleManager.FindByIdAsync(id); string[] memberIDs = role.Users.Select(x => x.UserId).ToArray(); IEnumerable<AppUser> members = UserManager.Users.Where(x => memberIDs.Any(y => y == x.Id)); IEnumerable<AppUser> nonMembers = UserManager.Users.Except(members); return View(new RoleEditModel { Role = role, Members = members, NonMembers = nonMembers }); }
[HttpPost] public async Task<ActionResult> Edit(RoleModificationModel model) { IdentityResult result; if (ModelState.IsValid) { foreach (string userId in model.IdsToAdd ?? new string[] { }) { result = await UserManager.AddToRoleAsync(userId, model.RoleName); if (!result.Succeeded) { return View("Error", result.Errors); } } foreach (string userId in model.IdsToDelete ?? new string[] { }) { result = await UserManager.RemoveFromRoleAsync(userId, model.RoleName); if (!result.Succeeded) { return View("Error", result.Errors); } } return RedirectToAction("Index"); } return View("Error", new string[] { "Role Not Found" }); }
private void AddErrorsFromResult(IdentityResult result) { foreach (string error in result.Errors) { ModelState.AddModelError("", error); } }
private AppUserManager UserManager { get { return HttpContext.GetOwinContext().GetUserManager<AppUserManager>(); } }
private AppRoleManager RoleManager { get { return HttpContext.GetOwinContext().GetUserManager<AppRoleManager>(); } } } }
The majority of the code in the GET version of the Edit action method is responsible for generating the sets of members and nonmembers of the selected role, which is done using LINQ. Once I have grouped the users, I call the View method, passing a new instance of the RoleEditModel class I defined in Listing 14-13.
GET版Edit動做方法的主要代碼是負責生成一組所選角色的成員和非成員,這是用LINQ完成的。一旦對用戶進行了分組,便調用View方法,爲其傳遞了清單14-13所定義的RoleEditModel類的新實例。
The POST version of the Edit method is responsible for adding and removing users to and from roles. The AppUserManager class inherits a number of role-related methods from its base class, which I have described in Table 14-9.
POST版的Edit方法是負責從角色中添加和刪除用戶。AppUserManager類從它的基類繼承了幾個與角色有關的方法,描述於表14-9。
Name 名稱 |
Description 描述 |
---|---|
AddToRoleAsync(id, name) | Adds the user with the specified ID to the role with the specified name 將指定ID的用戶添加到指定name的角色 |
GetRolesAsync(id) | Returns a list of the names of the roles of which the user with the specified ID is a member 返回指定ID的用戶所在的角色名列表 |
IsInRoleAsync(id, name) | Returns true if the user with the specified ID is a member of the role with the specified name 若是指定ID的用戶是指定name角色的成員,返回true |
RemoveFromRoleAsync(id, name) | Removes the user with the specified ID as a member from the role with the specified name 在指定name角色的成員中除去指定ID的用戶 |
An oddity of these methods is that the role-related methods operate on user IDs and role names, even though roles also have unique identifiers. It is for this reason that my RoleModificationModel view model class has a RoleName property.
這些方法的奇怪之處在於,與角色有關的方法都根據「用戶ID」和「角色name(角色名)」進行操做,儘管角色也具備惟一標識符(ID)。這也是在RoleModificationModel視圖模型類中使用RoleName屬性的緣由。
Listing 14-15 shows the view for the Edit.cshtml file, which I added to the Views/RoleAdmin folder and used to define the markup that allows the user to edit role memberships.
清單14-15顯示了Edit.cshtml文件的視圖,該視圖放在Views/RoleAdmin文件中,用它定義了讓用戶編輯角色成員的標記。
Listing 14-15. The Contents of the Edit.cshtml File in the Views/RoleAdmin Folder
清單14-15. Views/RoleAdmin文件夾中Edit.cshtml文件的內容
@using Users.Models @model RoleEditModel @{ ViewBag.Title = "Edit Role";} @Html.ValidationSummary() @using (Html.BeginForm()) { <input type="hidden" name="roleName" value="@Model.Role.Name" /> <div class="panel panel-primary"> <div class="panel-heading">Add To @Model.Role.Name</div> <table class="table table-striped"> @if (Model.NonMembers.Count() == 0) { <tr><td colspan="2">All Users Are Members</td></tr> } else { <tr><td>User ID</td><td>Add To Role</td></tr> foreach (AppUser user in Model.NonMembers) { <tr> <td>@user.UserName</td> <td> <input type="checkbox" name="IdsToAdd" value="@user.Id"> </td> </tr> } } </table> </div> <div class="panel panel-primary"> <div class="panel-heading">Remove from @Model.Role.Name</div> <table class="table table-striped"> @if (Model.Members.Count() == 0) { <tr><td colspan="2">No Users Are Members</td></tr> } else { <tr><td>User ID</td><td>Remove From Role</td></tr> foreach (AppUser user in Model.Members) { <tr> <td>@user.UserName</td> <td> <input type="checkbox" name="IdsToDelete" value="@user.Id"> </td> </tr> } } </table> </div> <button type="submit" class="btn btn-primary">Save</button> @Html.ActionLink("Cancel", "Index", null, new { @class = "btn btn-default" }) }
The view contains two tables: one for users who are not members of the selected role and one for those who are members. Each user’s name is displayed along with a check box that allows the membership to be changed.
該視圖含有兩個表格:一個用於不是所選角色成員的用戶,一個是所選角色成員的用戶。每一個被顯示出來的用戶名稱旁邊都有一個複選框,能夠修改其成員狀況。
Adding the AppRoleManager class to the application causes the Entity Framework to delete the contents of the database and rebuild the schema, which means that any users you created in the previous chapter have been removed. So that there are users to assign to roles, start the application and navigate to the /Admin/Index URL and create users with the details in Table 14-10.
在應用程序中添加AppRoleManager類會致使Entity Framework刪除數據庫的內容,並重建數據庫架構,這意味着在上一章建立的用戶都會被刪除。所以,爲了有用戶能夠賦予角色,需啓動應用程序並導航到/Admin/Index URL,先建立一些如表14-10所示的用戶。
Name 用戶名 |
Email |
Password 口令 |
---|---|---|
Alice | alice@example.com | MySecret |
Bob | bob@example.com | MySecret |
Joe | joe@example.com | MySecret |
Tip deleting the user database is fine for an example application but tends to be a problem in real applications. I show you how to gracefully manage changes to the database schema in Chapter 15.
提示:刪除用戶對示例應用程序而言沒什麼問題,但對實際應用程序來講就是一個問題了。第15章將演示如何優雅地修改數據庫架構。
To test managing role memberships, navigate to the /RoleAdmin/Index URL and create a role called Users, following the instructions from the 「Testing, Creating, and Deleting Roles」 section. Click the Edit button and check the boxes so that Alice and Joe are members of the role but Bob is not, as shown in Figure 14-5.
爲了測試角色成員的管理,導航到/RoleAdmin/Index URL,並按照「測試角色的建立和刪除」小節的說明,建立一個名稱爲Users的角色。點擊「Edit」按鈕,並選中複選框,使Alice和Joe成爲該角色的成員,而Bob別選,如圖14-5所示。
Figure 14-5. Editing role membership
圖14-5. 編輯角色成員
Tip If you get an error that tells you there is already an open a data reader, then you didn’t set the MultipleActiveResultSets setting to true in the connection string in Chapter 13.
提示:若是出現錯誤,告訴你說,已經有一個打開的數據讀取程序,那是由於你並未將第13章鏈接字符串中的MultipleActiveResultSets設置爲true。
Click the Save button, and the controller will update the role memberships and redirect the browser to the Index action. The summary of the Users role will show that Alice and Joe are now members, as illustrated by Figure 14-6.
點擊「Save」按鈕,因而控制器將更新角色成員,並將瀏覽器重定向到Index動做。Users角色的摘要將顯示Alice和Joe如今已是成員,如圖14-6所示。
Figure 14-6. The effect of adding users to a role
圖14-6. 將用戶添加到角色的效果
Now that I have the ability to manage roles, I can use them as the basis for authorization through the Authorize attribute. To make it easier to test role-based authorization, I have added a Logout method to the Account controller, as shown in Listing 14-16, which will make it easier to log out and log in again as a different user to see the effect of role membership.
如今已經可以管理角色了,經過Authorize註解屬性,還能夠將角色做爲受權的基礎。爲了更易於測試基於角色的受權,我在Account控制器中添加了一個Logout方法,如清單14-16所示,這樣便很容易註銷,也容易做爲不一樣用戶登陸,以看看角色成員的效果。
Listing 14-16. Adding a Logout Method to the AccountController.cs File
清單14-16. 在AccountController.cs文件中添加Logout方法
using System.Threading.Tasks; using System.Web.Mvc; using Users.Models; using Microsoft.Owin.Security; using System.Security.Claims; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Users.Infrastructure; using System.Web;
namespace Users.Controllers {
[Authorize] public class AccountController : Controller {
[AllowAnonymous] public ActionResult Login(string returnUrl) { ViewBag.returnUrl = returnUrl; return View(); }
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginModel details, string returnUrl) { // ...statements omitted for brevity... // ...出於簡化,忽略了一些語句... }
[Authorize] public ActionResult Logout() { AuthManager.SignOut(); return RedirectToAction("Index", "Home"); }
private IAuthenticationManager AuthManager { get { return HttpContext.GetOwinContext().Authentication; } }
private AppUserManager UserManager { get { return HttpContext.GetOwinContext().GetUserManager<AppUserManager>(); } } } }
I have updated the Home controller to add a new action method and pass some information about the authenticated user to the view, as shown in Listing 14-17.
我也更新了Home控制器,添加了一個新的動做方法,並將已認證用戶的一些信息傳遞給視圖,如清單14-17所示。
Listing 14-17. Adding an Action Method and Account Information to the HomeController.cs File
清單14-17. 在HomeController.cs文件中添加動做方法和帳號信息
using System.Web.Mvc; using System.Collections.Generic; using System.Web; using System.Security.Principal;
namespace Users.Controllers {
public class HomeController : Controller {
[Authorize] public ActionResult Index() { return View(GetData("Index")); }
[Authorize(Roles="Users")] public ActionResult OtherAction() { return View("Index", GetData("OtherAction")); }
private Dictionary<string, object> GetData(string actionName) { Dictionary<string, object> dict = new Dictionary<string, object>(); dict.Add("Action", actionName); dict.Add("User", HttpContext.User.Identity.Name); dict.Add("Authenticated", HttpContext.User.Identity.IsAuthenticated); dict.Add("Auth Type", HttpContext.User.Identity.AuthenticationType); dict.Add("In Users Role", HttpContext.User.IsInRole("Users")); return dict; } } }
I have left the Authorize attribute unchanged for the Index action method, but I have set the Roles property when applying the attribute to the OtherAction method, specifying that only members of the Users role should be able to access it. I also defined a GetData method, which adds some basic information about the user identity, using the properties available through the HttpContext object. The final change I made was to the Index.cshtml file in the Views/Home folder, which is used by both actions in the Home controller, to add a link that targets the Logout method in the Account controller, as shown in Listing 14-18.
我沒有改變Index動做方法上的Authorize註解屬性,但將該屬性運用於OtherAction方法時,已經設置了Roles屬性,指明只有Users角色的成員纔可以訪問它。我還定義了一個GetData方法,它添加了一些有關用戶標識的基本信息,這是經過HttpContext對象可用的屬性得到的。最後所作的修改是Views/Home文件夾中的Index.cshtml文件,它是由Home控制器中的兩個動做使用的,我在其中添加了一些以Account控制器中的Logout方法爲目標的連接,如清單14-18所示。
Listing 14-18. Adding a Sign-Out Link to the Index.cshtml File in the Views/Home Folder
清單14-18. 在Views/Home文件夾中的Index.cshtml文件中添加Sign-Out(簽出)連接
@{ ViewBag.Title = "Index"; }
<div class="panel panel-primary"> <div class="panel-heading">User Details</div> <table class="table table-striped"> @foreach (string key in Model.Keys) { <tr> <th>@key</th> <td>@Model[key]</td> </tr> } </table> </div>
@Html.ActionLink("Sign Out", "Logout", "Account", null, new {@class = "btn btn-primary"})
Tip the Authorize attribute can also be used to authorize access based on a list of individual usernames. This is an appealing feature for small projects, but it means you have to change the code in your controllers each time the set of users you are authorizing changes, and that usually means having to go through the test-and-deploy cycle again. Using roles for authorization isolates the application from changes in individual user accounts and allows you to control access to the application through the memberships stored by ASP.NET Identity.
提示:Authorize註解屬性也可以用來根據個別用戶名進行受權訪問。這是一個對小型項目很吸引人的特性,但這意味着,你每次受權的用戶集合發生變化時,必須修改控制器中的代碼,這也意味着,要重走一遍從測試到部署的開發週期。使用角色受權將應用程序與修改個別用戶帳號隔離開來,使你可以經過ASP.NET Identity存儲的成員來控制對應用程序的訪問。
To test the authentication, start the application and navigate to the /Home/Index URL. Your browser will be redirected so that you can enter user credentials. It doesn’t matter which of the user details from Table 14-10 you choose to authenticate with because the Authorize attribute applied to the Index action allows access to any authenticated user.
爲了測試認證,啓動應用程序,並導航到/Home/Index URL。瀏覽器將被重定向,讓你輸入用戶憑據。選用表14-10中的哪個用戶細節進行認證沒有多大關係,由於運用於Index動做的Authorize註解屬性容許任何已認證用戶進行訪問
However, if you now request the /Home/OtherAction URL, the user details you chose from Table 14-10 will make a difference because only Alice and Joe are members of the Users role, which is required to access the OtherAction method.
然而,若是你如今請求/Home/OtherAction URL,從表14-10所選的用戶細節就有區別了,由於只有Alice和Joe是Users角色的成員,這是訪問OtherAction方法所必須的。
If you log in as Bob, then your browser will be redirected so that you can be prompted for credentials once again.
若是以Bob登陸,那麼瀏覽器將被重定向,可能會提示再次輸入憑據。
Redirecting an already authenticated user for more credentials is rarely a useful thing to do, so I have modified the Login action method in the Account controller to check to see whether the user is authenticated and, if so, redirect them to the shared Error view. Listing 14-19 shows the changes.
重定向已認證用戶要求更多憑據幾乎是一件毫無做用的事,所以,我修改了Account控制器中的Login動做方法,檢查用戶是否已認證,若是是,則將他們重定向到共享的Error視圖,清單14-19顯示了所作的修改。
Listing 14-19. Detecting Already Authenticated Users in the AccountController.cs File
清單14-19. 在AccountController.cs文件中檢測已認證用戶
using System.Threading.Tasks; using System.Web.Mvc; using Users.Models; using Microsoft.Owin.Security; using System.Security.Claims; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Users.Infrastructure; using System.Web;
namespace Users.Controllers {
[Authorize] public class AccountController : Controller {
[AllowAnonymous] public ActionResult Login(string returnUrl) { if (HttpContext.User.Identity.IsAuthenticated) { return View("Error", new string[] { "Access Denied" }); } ViewBag.returnUrl = returnUrl; return View(); }
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginModel details, string returnUrl) { // ...code omitted for brevity... // ...出於簡化,忽略了這裏的代碼... }
[Authorize] public ActionResult Logout() { AuthManager.SignOut(); return RedirectToAction("Index", "Home"); }
private IAuthenticationManager AuthManager { get { return HttpContext.GetOwinContext().Authentication; } }
private AppUserManager UserManager { get { return HttpContext.GetOwinContext().GetUserManager<AppUserManager>(); } } } }
Figure 14-7 shows the responses generated for the user Bob when requesting the /Home/Index and /Home/OtherAction URLs.
圖14-7顯示了用戶Bob在請求/Home/Index和/Home/OtherAction URL時生成的響應。
Figure 14-7. Using roles to control access to action methods
圖14-7. 使用角色控制對動做方法的訪問
Tip Roles are loaded when the user logs in, which means if you change the roles for the user you are currently authenticated as, the changes won’t take effect until you log out and authenticate.
提示:角色在用戶登陸時就會加載,這意味着,若是修改了當前已認證用戶的角色,這些修改是不會生效的,直到他們退出並從新認證。
One lingering problem in my example project is that access to my Admin and RoleAdmin controllers is not restricted.
上述示例項目中一直未消除的一個問題是,對Admin和RoleAdmin控制器的訪問是不受限制的。
This is a classic chicken-and-egg problem because in order to restrict access, I need to create users and roles, but the Admin and RoleAdmin controllers are the user management tools, and if I protect them with the Authorize attribute, there won’t be any credentials that will grant me access to them, especially when I first deploy the application.
這是一個經典的雞與蛋的問題,由於,若要限制訪問,則須要預先建立一些用戶和角色,但Admin和RoleAdmin控制器又是用戶管理工具,若是用Authorize註解屬性來保護它們,那麼就不存在可以對它們訪問的憑據,特別是在第一次部署應用程序時。
The solution to this problem is to seed the database with some initial data when the Entity Framework Code First feature creates the schema. This allows me to automatically create users and assign them to roles so that there is a base level of content available in the database.
這一問題的解決方案是,在Entity Framework的Code First特性建立數據庫架構時,以一些初始的數據植入數據庫。這樣可以自動地建立一些用戶,並賦予必定的角色,以使數據庫中有一個基礎級的內容可用。
The database is seeded by adding statements to the PerformInitialSetup method of the IdentityDbInit class, which is the application-specific Entity Framework database setup class. Listing 14-20 shows the changes I made to create an administration user.
種植數據庫的辦法是在IdentityDbInit類的PerformInitialSetup方法中添加一些語句,IdentityDbInit是應用程序專用的Entity Framework數據庫設置類。清單14-20是爲了建立管理用戶所作的修改
Listing 14-20. Seeding the Database in the AppIdentityDbContext.cs File
清單14-20. 在AppIdentityDbContext.cs文件中種植數據庫
using System.Data.Entity; using Microsoft.AspNet.Identity.EntityFramework; using Users.Models; using Microsoft.AspNet.Identity;
namespace Users.Infrastructure { public class AppIdentityDbContext : IdentityDbContext<AppUser> {
public AppIdentityDbContext() : base("IdentityDb") { }
static AppIdentityDbContext() { Database.SetInitializer<AppIdentityDbContext>(new IdentityDbInit()); }
public static AppIdentityDbContext Create() { return new AppIdentityDbContext(); } }
public class IdentityDbInit : DropCreateDatabaseIfModelChanges<AppIdentityDbContext> { protected override void Seed(AppIdentityDbContext context) { PerformInitialSetup(context); base.Seed(context); }
public void PerformInitialSetup(AppIdentityDbContext context) { AppUserManager userMgr = new AppUserManager(new UserStore<AppUser>(context)); AppRoleManager roleMgr = new AppRoleManager(new RoleStore<AppRole>(context));
string roleName = "Administrators"; string userName = "Admin"; string password = "MySecret"; string email = "admin@example.com";
if (!roleMgr.RoleExists(roleName)) { roleMgr.Create(new AppRole(roleName)); }
AppUser user = userMgr.FindByName(userName); if (user == null) { userMgr.Create(new AppUser { UserName = userName, Email = email }, password); user = userMgr.FindByName(userName); }
if (!userMgr.IsInRole(user.Id, roleName)) { userMgr.AddToRole(user.Id, roleName); } } } }
Tip For this example, I used the synchronous extension methods to locate and manage the role and user. As I explained in Chapter 13, I prefer the asynchronous methods by default, but the synchronous methods can be useful when you need to perform a sequence of related operations.
提示:在上述示例中,我使用了同步的擴展方法來定位和管理角色和用戶。正如第13章所解釋的那樣,通常狀況下我更喜歡異步方法,可是,當須要執行一系列相關操做時,同步方法多是有用的。
I have to create instances of AppUserManager and AppRoleManager directly because the PerformInitialSetup method is called before the OWIN configuration is complete. I use the RoleManager and AppManager objects to create a role called Administrators and a user called Admin and add the user to the role.
我必須直接建立AppUserManager和AppRoleManager的實例,由於PerformInitialSetup方法是在OWIN配置完成以前就被調用的。我使用RoleManager和AppManager對象建立一個名稱爲Administrators的角色,和一個名稱爲的Admin的用戶,並將此用戶添加到該角色。
Tip Read Chapter 15 before you add database seeding to your project. I describe database migrations, which allow you to take control of schema changes in the database and which put the seeding logic in a different place.
提示:在項目中添加數據庫種植以前,請先閱讀第15章。我在其中描述了數據庫遷移,這讓你可以對數據庫中的架構變化進行控制,並能夠將種植邏輯放在不一樣的地方。
With this change, I can use the Authorize attribute to protect the Admin and RoleAdmin controllers. Listing 14-21 shows the change I made to the Admin controller.
通過這種修改,我可使用Authorize註解屬性來保護Admin和RoleAdmin控制器。清單14-21顯示了對Admin控制器所作的修改。
Listing 14-21. Restricting Access in the AdminController.cs File
清單14-21. 在AdminController.cs文件中的限制訪問
using System.Web; using System.Web.Mvc; using Microsoft.AspNet.Identity.Owin; using Users.Infrastructure; using Users.Models; using Microsoft.AspNet.Identity; using System.Threading.Tasks;
namespace Users.Controllers {
[Authorize(Roles = "Administrators")] public class AdminController : Controller { // ...statements omitted for brevity... // ...出於簡化,忽略了這裏的語句... } }
Listing 14-22 shows the corresponding change I made to the RoleAdmin controller.
清單14-22是對RoleAdmin控制器所作的相應修改。
Listing 14-22. Restricting Access in the RoleAdminController.cs File
清單14-22. 在RoleAdminController.cs文件中的限制訪問
using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using System.Web; using System.Web.Mvc; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Users.Infrastructure; using Users.Models; using System.Collections.Generic;
namespace Users.Controllers {
[Authorize(Roles = "Administrators")] public class RoleAdminController : Controller { // ...statements omitted for brevity... // ...出於簡化,忽略了這裏的語句... } }
The database is seeded only when the schema is created, which means I need to reset the database to complete the process. This isn’t something you would do in a real application, of course, but I wanted to wait until I demonstrated how authentication and authorization worked before creating the administrator account.
只有在建立架構時纔會種植數據庫,這意味着須要重置數據庫才能完成這一過程。固然,這不是在實際項目中可能要作的事情,但我但願等一等,在建立管理員帳號以前,完成認證與受權的演示。
To delete the database, open the Visual Studio SQL Server Object Explorer window and locate and right-click the IdentityDb item. Select Delete from the pop-up menu and check both of the options in the Delete Database dialog window. Click the OK button to delete the database.
爲了刪除數據庫,請打開Visual Studio中的「SQL Server對象資源管理器」窗口,找到並右擊「IdentityDb」條目。從彈出菜單選擇「Delete(刪除)」,並在「Delete Database(刪除數據庫)」窗口選中那兩個選項。點擊「OK」按鈕,刪除該數據庫。
Now create an empty database to which the schema will be added by right-clicking the Databases item, selecting Add New Database, and entering IdentityDb in the Database Name field. Click OK to create the empty database.
如今,右擊「Databases(數據庫)」條目,選擇「Add New Database(添加新數據庫)」,並在「Database Name(數據庫名稱)」字段中輸入IdentityDb。點擊OK,建立一個空數據庫。
Tip There are step-by-step instructions with screenshots in Chapter 13 for creating the database.
提示:第13章有建立數據庫的逐步說明和屏幕截圖。
Now start the application and request the /Admin/Index or /RoleAdmin/Index URL. There will be a delay while the schema is created and the database is seeded, and then you will be prompted to enter your credentials. Use Admin as the name and MySecret as the password, and you will be granted access to the controllers.
如今,啓動應用程序,請求/Admin/Index或/RoleAdmin/Index URL。在建立數據庫架構以及植入數據庫期間會有一點延時,而後將提示你輸入憑據。使用Admin做爲用戶名,MySecret做爲口令,將會得到對該控制器的訪問。
Caution Deleting the database removes the user accounts you created using the details in table 14-10, which is why you would not perform this task on a live database containing user details.
警告:刪除數據庫也刪去了你用表14-10所建立的用戶帳號,正是這一緣由,通常不會在一個含有用戶細節的活動數據庫中執行此項任務。
In this chapter, I showed you how to use ASP.NET Identity to authenticate and authorize users. I explained how the ASP.NET life-cycle events provide a foundation for authenticating requests, how to collect and validate credentials users, and how to restrict access to action methods based on the roles that a user is a member of. In the next chapter, I demonstrate some of the advanced features that ASP.NET Identity provides.
在本章中,我演示瞭如何使用ASP.NET Identity進行用戶認證與受權。解釋了ASP.NET生命週期事件如何提供認證基礎,如何收集和檢驗用戶憑據,以及如何根據用戶的成員角色限制對動做方法的訪問。下一章將演示ASP.NET Identity所提供的一些高級特性。
看完此文若是以爲有所收穫,請給個推薦。 你的推薦是我繼續下去的動力,也會讓更多人關注並獲益,這也是你的貢獻。