Asp.Net Core 5 REST API 使用 JWT 身份验证 - Step by Step(二)
翻譯自 Mohamad Lawand 2021年1月22日的文章?《Asp Net Core 5 Rest API Authentication with JWT Step by Step》?[1]
在本文中,我將向您展示如何向我們的 Asp.Net Core REST API 添加 JWT 身份驗(yàn)證。
我們將介紹的主題包含注冊(cè)、登錄功能以及如何使用?JWT (Json Web Tokens)[2]和 Bearer 身份驗(yàn)證。
你也可以在 YouTube 上觀看完整的視頻[3],還可以下載源代碼[4]。
這是 API 開發(fā)系列的第二部分,本系列還包含:
Part 1:Asp.Net Core 5 REST API - Step by Step
Part 3:Asp Net Core 5 REST API 中使用 RefreshToken 刷新 JWT - Step by Step
我們將基于上一篇文章中創(chuàng)建的 Todo REST API 應(yīng)用程序進(jìn)行當(dāng)前的講述,您可以通過閱讀上一篇文章并與我一起構(gòu)建應(yīng)用程序,或者可以從 github 下載上一篇中的源代碼。
前一篇文章中的代碼準(zhǔn)備好以后,就讓我們開始本文吧。
首先,我們需要安裝一些依賴包以使用身份驗(yàn)證:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore dotnet add package Microsoft.AspNetCore.Identity.UI然后,我們需要更新?appsettings.json,在?appsettings?中添加 JWT 的設(shè)置部分,在該設(shè)置中添加一個(gè) JWT secret(密鑰)。
"JwtConfig": {"Secret" : "ijurkbdlhmklqacwqzdxmkkhvqowlyqa" },為了生成 secret,我們可以使用一個(gè)免費(fèi)的 Web 工具(https://www.browserling.com/tools/random-string)來生成一個(gè)隨機(jī)的 32 個(gè)字符的字符串。
我們?cè)?appsettings?中添加完隨機(jī)生成的 32 個(gè)字符的字符串后,接著需要在根目錄中創(chuàng)建一個(gè)名為?Configuration?的新文件夾。
在這個(gè)?Configuration?文件夾中,我們將創(chuàng)建一個(gè)名為?JwtConfig?的新類:
public class JwtConfig {public string Secret { get; set; } }現(xiàn)在我們需要更新?Startup?類,在?ConfigureServices?方法內(nèi),我們需要添加以下內(nèi)容,以便將 JWT 配置注入到應(yīng)用程序中:
services.Configure<JwtConfig>(Configuration.GetSection("JwtConfig"));將這些配置添加到我們的?Startup?類中,即可在 Asp.Net Core 中間件和 IoC 容器中注冊(cè)配置。
下一步是在我們的?Startup?類中添加和配置身份驗(yàn)證,在我們的?ConfigureServices?方法中,我們需要添加以下內(nèi)容:
// 在本段中,我們將配置身份驗(yàn)證并設(shè)置默認(rèn)方案 services.AddAuthentication(options => {options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(jwt => {var key = Encoding.ASCII.GetBytes(Configuration["JwtConfig:Secret"]);jwt.SaveToken = true;jwt.TokenValidationParameters = new TokenValidationParameters {ValidateIssuerSigningKey = true, //這將使用我們?cè)?appsettings 中添加的 secret 來驗(yàn)證 JWT token 的第三部分,并驗(yàn)證 JWT token 是由我們生成的IssuerSigningKey = new SymmetricSecurityKey(key), //將密鑰添加到我們的 JWT 加密算法中ValidateIssuer = false,ValidateAudience = false,ValidateLifetime = true,RequireExpirationTime = false}; });services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true).AddEntityFrameworkStores<ApiDbContext>();更新好?ConfigureServices?之后,我們需要更新?Configure?方法,添加身份驗(yàn)證:
app.UseAuthentication();配置添加完成后,我們需要構(gòu)建應(yīng)用程序,檢查是否所有的內(nèi)容都可以正常構(gòu)建:
dotnet build dotnet run下一步是更新我們的?ApiDbContext,以便使用 Asp.Net 為我們提供的身份提供程序,導(dǎo)航到?Data?文件夾中的ApiDbContext,然后按以下內(nèi)容更新?ApiDbContext?類:
public class ApiDbContext : IdentityDbContext通過從?IdentityDbContext?而不是?DbContext?繼承,EntityFramework 將知道我們正在使用身份驗(yàn)證,并且將為我們構(gòu)建基礎(chǔ)設(shè)施以使用默認(rèn)身份表。
要在我們的數(shù)據(jù)庫中生成身份表,我們需要準(zhǔn)備遷移腳本并運(yùn)行它們。也就是說,我們需要在終端中輸入并運(yùn)行以下命令:
dotnet ef migrations add "Adding authentication to our Api" dotnet ef database update遷移完成后,我們可以使用 Dbeaver 打開數(shù)據(jù)庫?app.db,我們可以看到 EntityFramework 已經(jīng)為我們創(chuàng)建了身份表。
下一步是設(shè)置控制器并為用戶構(gòu)建注冊(cè)流程。我們需要在?Controllers?文件夾中創(chuàng)建一個(gè)新的控制器,并創(chuàng)建對(duì)應(yīng)的 DTO 類(Data Transfer Objects)。
先在根目錄中的?Configuration?文件夾中添加一個(gè)名為?AuthResult?的類:
// Configuration\AuthResult.cspublic class AuthResult {public string Token { get; set; }public bool Success { get; set; }public List<string> Errors { get; set; } }然后我將添加一些文件夾來組織 DTOs,在?Models?文件夾中添加一個(gè)名為?DTOs?的文件夾,然后在此文件夾中創(chuàng)建兩個(gè)子文件夾?Requests?和?Responses。
我們需要添加供我們?cè)诳刂破髦械淖?cè) Action 使用的?UserRegistrationDto。導(dǎo)航到?Models/DTO/Requests,添加一個(gè)新類?UserRegistrationDto。
// Models\DTOs\Requests\UserRegistrationDto.cspublic class UserRegistrationDto {[Required]public string Username { get; set; }[Required][EmailAddress]public string Email { get; set; }[Required]public string Password { get; set; } }添加?RegistrationResponse?響應(yīng)類。
// Models\DTOs\Responses\RegistrationResponse.cspublic class RegistrationResponse : AuthResult {}現(xiàn)在,我們需要添加用戶注冊(cè)控制器,在控制器文件夾中添加一個(gè)新類,命名為?AuthManagementController,并使用以下代碼更新它:
// Controllers\AuthManagementController.cs[Route("api/[controller]")] // api/authmanagement [ApiController] public class AuthManagementController : ControllerBase {private readonly UserManager<IdentityUser> _userManager;private readonly JwtConfig _jwtConfig;public AuthManagementController(UserManager<IdentityUser> userManager, IOptionsMonitor<JwtConfig> optionsMonitor){_userManager = userManager;_jwtConfig = optionsMonitor.CurrentValue;}[HttpPost][Route("Register")]public async Task<IActionResult> Register([FromBody] UserRegistrationDto user){// 檢查傳入請(qǐng)求是否有效if(ModelState.IsValid){// 檢查使用相同電子郵箱的用戶是否存在var existingUser = await _userManager.FindByEmailAsync(user.Email);if(existingUser != null){return BadRequest(new RegistrationResponse(){Errors = new List<string>() {"Email already in use"},Success = false});}var newUser = new IdentityUser() { Email = user.Email, UserName = user.Username };var isCreated = await _userManager.CreateAsync(newUser, user.Password);if(isCreated.Succeeded){var jwtToken = GenerateJwtToken( newUser);return Ok(new RegistrationResponse() {Success = true,Token = jwtToken});} else {return BadRequest(new RegistrationResponse(){Errors = isCreated.Errors.Select(x => x.Description).ToList(),Success = false});}}return BadRequest(new RegistrationResponse(){Errors = new List<string>() {"Invalid payload"},Success = false});}private string GenerateJwtToken(IdentityUser user){//現(xiàn)在,是時(shí)候定義 jwt token 了,它將負(fù)責(zé)創(chuàng)建我們的 tokensvar jwtTokenHandler = new JwtSecurityTokenHandler();// 從 appsettings 中獲得我們的 secret var key = Encoding.ASCII.GetBytes(_jwtConfig.Secret);// 定義我們的 token descriptor// 我們需要使用 claims (token 中的屬性)給出關(guān)于 token 的信息,它們屬于特定的用戶,// 因此,可以包含用戶的 Id、名字、郵箱等。// 好消息是,這些信息由我們的服務(wù)器和 Identity framework 生成,它們是有效且可信的。var tokenDescriptor = new SecurityTokenDescriptor{Subject = new ClaimsIdentity(new []{new Claim("Id", user.Id), new Claim(JwtRegisteredClaimNames.Email, user.Email),new Claim(JwtRegisteredClaimNames.Sub, user.Email),// Jti 用于刷新 token,我們將在下一篇中講到new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())}),// token 的過期時(shí)間需要縮短,并利用 refresh token 來保持用戶的登錄狀態(tài),// 不過由于這只是一個(gè)演示應(yīng)用,我們可以對(duì)其進(jìn)行延長(zhǎng)以適應(yīng)我們當(dāng)前的需求Expires = DateTime.UtcNow.AddHours(6),// 這里我們添加了加密算法信息,用于加密我們的 tokenSigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)};var token = jwtTokenHandler.CreateToken(tokenDescriptor);var jwtToken = jwtTokenHandler.WriteToken(token);return jwtToken;} }添加完注冊(cè)的 Action 后,我們可以在 Postman 中對(duì)其進(jìn)行測(cè)試并獲得 JWT token。
接下來是創(chuàng)建用戶登錄請(qǐng)求:
// Models\DTOs\Requests\UserLoginRequest.cspublic class UserLoginRequest {[Required][EmailAddress]public string Email { get; set; }[Required]public string Password { get; set; } }然后,我們需要在?AuthManagementController?中添加?Login?方法:
[HttpPost] [Route("Login")] public async Task<IActionResult> Login([FromBody] UserLoginRequest user) {if(ModelState.IsValid){// 檢查使用相同電子郵箱的用戶是否存在var existingUser = await _userManager.FindByEmailAsync(user.Email);if(existingUser == null) {// 出于安全原因,我們不想透露太多關(guān)于請(qǐng)求失敗的信息return BadRequest(new RegistrationResponse(){Errors = new List<string>() {"Invalid login request"},Success = false});}// 現(xiàn)在我們需要檢查用戶是否輸入了正確的密碼var isCorrect = await _userManager.CheckPasswordAsync(existingUser, user.Password);if(!isCorrect) {// 出于安全原因,我們不想透露太多關(guān)于請(qǐng)求失敗的信息return BadRequest(new RegistrationResponse(){Errors = new List<string>() {"Invalid login request"},Success = false});}var jwtToken = GenerateJwtToken(existingUser);return Ok(new RegistrationResponse() {Success = true,Token = jwtToken});}return BadRequest(new RegistrationResponse(){Errors = new List<string>() {"Invalid payload"},Success = false}); }現(xiàn)在,我們可以在 Postman 中對(duì)其進(jìn)行測(cè)試,我們將會(huì)看到 JWT token 已經(jīng)成功生成。
下一步是保護(hù)我們的控制器,需要做的就是向控制器添加?Authorize?屬性。
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [Route("api/[controller]")] // api/todo [ApiController] public class TodoController : ControllerBase此時(shí),如果我們?cè)賹?duì)?Todo?進(jìn)行測(cè)試,則由于未獲得授權(quán),我們將會(huì)無法執(zhí)行任何請(qǐng)求。為了發(fā)送帶授權(quán)的請(qǐng)求,我們需要添加帶有?Bearer token?的授權(quán) Header,以便 Asp.Net 可以驗(yàn)證它,并授予我們執(zhí)行操作的權(quán)限。
譯者注:
添加 Bearer token 請(qǐng)求頭的方法是:在 Headers 中,添加一個(gè)名稱為?Authorization?的 Header 項(xiàng),值為?Bearer <token>(需將?<token>?替換為真實(shí)的 token 值)。使用 Postman 測(cè)試時(shí),可參考 Postman 官方文檔:https://learning.postman.com/docs/sending-requests/authorization/#bearer-token。
至此,我們已經(jīng)完成了使用 JWT 為 REST API 添加身份驗(yàn)證的功能。
感謝您花時(shí)間閱讀本文。
本文是 API 開發(fā)系列的第二部分,本系列還包含:
Part 1:Asp.Net Core 5 REST API - Step by Step
Part 3:Asp Net Core 5 REST API 中使用 RefreshToken 刷新 JWT - Step by Step
相關(guān)鏈接:
https://dev.to/moe23/asp-net-core-5-rest-api-authentication-with-jwt-step-by-step-140d?Asp Net Core 5 Rest API Authentication with JWT Step by Step???
https://mp.weixin.qq.com/s/jnC8FDKm0Srj0ww-EvdUiw?JWT 介紹 - Step by Step???
https://youtu.be/LgpC4tYtc6Y???
https://github.com/mohamadlawand087/v7-RestApiNetCoreAuthentication???
作者 :Mohamad Lawand
譯者 :技術(shù)譯民
出品 :技術(shù)譯站(https://ITTranslator.cn/)
END
總結(jié)
以上是生活随笔為你收集整理的Asp.Net Core 5 REST API 使用 JWT 身份验证 - Step by Step(二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET 6 Preview 3 发布
- 下一篇: ASP.NET Core 5.0 Web