使用identity+jwt保护你的webapi(二)——获取jwt token
前言
上一篇已經(jīng)介紹了identity在web api中的基本配置,本篇來(lái)完成用戶的注冊(cè),登錄,獲取jwt token。
開(kāi)始
開(kāi)始之前先配置一下jwt相關(guān)服務(wù)。
配置JWT
首先NuGet安裝包:
<PackageReference?Include="Microsoft.AspNetCore.Authentication.JwtBearer"?Version="5.0.10"?/>appsettings.json中添加jwt配置:
"JwtSettings":?{"SecurityKey":?"qP1yR9qH2xS0vW2lA3gI4nF0zA7fA3hB","ExpiresIn":?"00:10:00" }為了方便,新建一個(gè)配置類JwtSettings:
public?class?JwtSettings {public?string?SecurityKey?{?get;?set;?}public?TimeSpan?ExpiresIn?{?get;?set;?} }在Startup中配置jwt:
public?void?ConfigureServices(IServiceCollection?services) {//省略......var?jwtSettings?=?Configuration.GetSection(nameof(JwtSettings)).Get<JwtSettings>();services.AddSingleton(jwtSettings);var?tokenValidationParameters?=?new?TokenValidationParameters{ValidateIssuer?=?false,ValidateAudience?=?false,ValidateIssuerSigningKey?=?true,IssuerSigningKey?=?new?SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtSettings.SecurityKey)),ClockSkew?=?TimeSpan.Zero,};services.AddAuthentication(options?=>{options.DefaultAuthenticateScheme?=?JwtBearerDefaults.AuthenticationScheme;options.DefaultScheme?=?JwtBearerDefaults.AuthenticationScheme;options.DefaultChallengeScheme?=?JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(options?=>?{?options.TokenValidationParameters?=?tokenValidationParameters;?}); }最后別忘了UseAuthentication:
app.UseAuthentication();?//?add app.UseAuthorization();結(jié)構(gòu)搭建
下面把項(xiàng)目基本結(jié)構(gòu)搭建好,做好接口,后面實(shí)現(xiàn):
以下是各個(gè)類的定義:
//?用戶注冊(cè)請(qǐng)求參數(shù) public?class?RegisterRequest {public?string?UserName?{?get;?set;?}public?string?Password?{?get;?set;?}public?string?Address?{?get;?set;?} }//?用戶登錄請(qǐng)求參數(shù) public?class?LoginRequest {public?string?UserName?{?get;?set;?}public?string?Password?{?get;?set;?} }//?注冊(cè)?登錄?成功后返回?token public?class?TokenResponse {[JsonPropertyName("access_token")]?public?string?AccessToken?{?get;?set;?}[JsonPropertyName("token_type")]?public?string?TokenType?{?get;?set;?} }//?登錄?注冊(cè)?失敗時(shí)返回錯(cuò)誤信息 public?class?FailedResponse {public?IEnumerable<string>?Errors?{?get;?set;?} }//?IUserService?接口 public?interface?IUserService {Task<TokenResult>?RegisterAsync(string?username,?string?password,?string?address);Task<TokenResult>?LoginAsync(string?username,?string?password); }//?UserService?實(shí)現(xiàn) public?class?UserService?:?IUserService {public?Task<TokenResult>?RegisterAsync(string?username,?string?password,?string?address){throw?new?System.NotImplementedException();}public?Task<TokenResult>?LoginAsync(string?username,?string?password){throw?new?System.NotImplementedException();} }//?TokenResult?定義 public?class?TokenResult {public?bool?Success?=>?Errors?==?null?||?!Errors.Any();public?IEnumerable<string>?Errors?{?get;?set;?}public?string?AccessToken?{?get;?set;?}public?string?TokenType?{?get;?set;?} }最后是UserController:
[Route("api/[controller]")] [ApiController] public?class?UserController?:?ControllerBase {private?readonly?IUserService?_userService;public?UserController(IUserService?userService){_userService?=?userService;}[HttpPost("Register")]public?async?Task<IActionResult>?Register(RegisterRequest?request){var?result?=?await?_userService.RegisterAsync(request.UserName,?request.Password,?request.Address);if?(!result.Success){return?BadRequest(new?FailedResponse(){Errors?=?result.Errors});}return?Ok(new?TokenResponse{AccessToken?=?result.AccessToken,TokenType?=?result.TokenType});}[HttpPost("Login")]public?async?Task<IActionResult>?Login(LoginRequest?request){var?result?=?await?_userService.LoginAsync(request.UserName,?request.Password);if?(!result.Success){return?Unauthorized(new?FailedResponse(){Errors?=?result.Errors});}return?Ok(new?TokenResponse{AccessToken?=?result.AccessToken,TokenType?=?result.TokenType});} }service實(shí)現(xiàn)
上面已經(jīng)做好了基本的結(jié)構(gòu),接下來(lái)就是實(shí)現(xiàn)UserService中的RegisterAsync和LoginAsync方法了。這里主要用到identity中的UserManager,UserManager封裝了很多用戶操作的現(xiàn)成方法。
在UserService中先做一個(gè)私有方法,根據(jù)user創(chuàng)建jwt token;用戶注冊(cè),登錄成功后調(diào)用此方法得到token返回即可:
private?TokenResult?GenerateJwtToken(AppUser?user) {var?key?=?Encoding.ASCII.GetBytes(_jwtSettings.SecurityKey);var?tokenDescriptor?=?new?SecurityTokenDescriptor{Subject?=?new?ClaimsIdentity(new[]{new?Claim(JwtRegisteredClaimNames.Jti,?Guid.NewGuid().ToString("N")),new?Claim(JwtRegisteredClaimNames.Sub,?user.Id.ToString())}),IssuedAt?=?DateTime.UtcNow,NotBefore?=?DateTime.UtcNow,Expires?=?DateTime.UtcNow.Add(_jwtSettings.ExpiresIn),SigningCredentials?=?new?SigningCredentials(new?SymmetricSecurityKey(key),SecurityAlgorithms.HmacSha256Signature)};var?jwtTokenHandler?=?new?JwtSecurityTokenHandler();var?securityToken?=?jwtTokenHandler.CreateToken(tokenDescriptor);var?token?=?jwtTokenHandler.WriteToken(securityToken);return?new?TokenResult(){AccessToken?=?token,TokenType?=?"Bearer"}; }注冊(cè)方法實(shí)現(xiàn):
public?async?Task<TokenResult>?RegisterAsync(string?username,?string?password,?string?address) {var?existingUser?=?await?_userManager.FindByNameAsync(username);if?(existingUser?!=?null){return?new?TokenResult(){Errors?=?new[]?{"user?already?exists!"},?//用戶已存在};}var?newUser?=?new?AppUser()?{UserName?=?username,?Address?=?address};var?isCreated?=?await?_userManager.CreateAsync(newUser,?password);if?(!isCreated.Succeeded){return?new?TokenResult(){Errors?=?isCreated.Errors.Select(p?=>?p.Description)};}return?GenerateJwtToken(newUser); }登錄方法實(shí)現(xiàn):
public?async?Task<TokenResult>?LoginAsync(string?username,?string?password) {var?existingUser?=?await?_userManager.FindByNameAsync(username);if?(existingUser?==?null){return?new?TokenResult(){Errors?=?new[]?{"user?does?not?exist!"},?//用戶不存在};}var?isCorrect?=?await?_userManager.CheckPasswordAsync(existingUser,?password);if?(!isCorrect){return?new?TokenResult(){Errors?=?new[]?{"wrong?user?name?or?password!"},?//用戶名或密碼錯(cuò)誤};}return?GenerateJwtToken(existingUser); }最后,別忘了注冊(cè)UserService:
services.AddScoped<IUserService,?UserService>();swagger配置
為了方便測(cè)試,可以配置一下swagger
NuGet安裝包:
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />ConfigureServices:
services.AddSwaggerGen(c?=> {c.SwaggerDoc("v1",?new?OpenApiInfo{Title?=?"Sample.Api",Version?=?"v1",Description?=?"Sample.Api?Swagger?Doc"});c.AddSecurityDefinition("Bearer",?new?OpenApiSecurityScheme{Description?=?"Input?the?JWT?like:?Bearer?{your?token}",Name?=?"Authorization",In?=?ParameterLocation.Header,Type?=?SecuritySchemeType.ApiKey,BearerFormat?=?"JWT",Scheme?=?"Bearer"});c.AddSecurityRequirement(new?OpenApiSecurityRequirement{{new?OpenApiSecurityScheme{Reference?=?new?OpenApiReference{Type?=?ReferenceType.SecurityScheme,Id?=?"Bearer"}},Array.Empty<string>()}}); });app.UseSwagger(); app.UseSwaggerUI(c?=>?c.SwaggerEndpoint("/swagger/v1/swagger.json",?"Sample.Api?v1"));測(cè)試一下
隨便輸入abc進(jìn)行注冊(cè),返回了一些密碼規(guī)則的錯(cuò)誤:
這個(gè)規(guī)則在注冊(cè)identity服務(wù)時(shí)可以配置:
services.AddIdentityCore<AppUser>(options?=> {options.Password.RequireDigit?=?true;options.Password.RequireLowercase?=?false;options.Password.RequireUppercase?=?false;options.Password.RequireNonAlphanumeric?=?false; }).AddEntityFrameworkStores<AppDbContext>();identityOptions還支持一些其他配置。
下面注冊(cè)成功后返回了token:
使用剛剛注冊(cè)的賬號(hào)測(cè)試登錄,也沒(méi)有問(wèn)題:
最后
本篇完成了identity的登錄,注冊(cè),獲取token,下一篇將介紹如何使用refresh token。
參考:
ASP.NET Core 簡(jiǎn)介 Identity | Microsoft Docs[1]
Mohamad Lawand - DEV Community[2]
參考資料
[1]
ASP.NET Core 簡(jiǎn)介 Identity | Microsoft Docs: https://docs.microsoft.com/zh-cn/aspnet/core/security/authentication/identity?view=aspnetcore-5.0&tabs=visual-studio
[2]Mohamad Lawand - DEV Community: https://dev.to/moe23/comments
總結(jié)
以上是生活随笔為你收集整理的使用identity+jwt保护你的webapi(二)——获取jwt token的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 使用identity+jwt保护你的we
- 下一篇: 再来说说我喜欢的 Dotnet 5.0