上一篇已经介绍了identity在web api中的基本配置,本篇来完成用户的注册,登录,获取jwt token。
开始之前先配置一下jwt相关服务。
首先NuGet安装包:
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.10" />
appsettings.json中添加jwt配置:
"JwtSettings": { "SecurityKey": "qP1yR9qH2xS0vW2lA3gI4nF0zA7fA3hB", "ExpiresIn": "00:10:00" }
为了方便,新建一个配置类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();
下面把项目基本结构搭建好,做好接口,后面实现:
以下是各个类的定义:
// 用户注册请求参数 public class RegisterRequest { public string UserName { get; set; } public string Password { get; set; } public string Address { get; set; } }
// 用户登录请求参数 public class LoginRequest { public string UserName { get; set; } public string Password { get; set; } }
// 注册 登录 成功后返回 token public class TokenResponse { [JsonPropertyName("access_token")] public string AccessToken { get; set; } [JsonPropertyName("token_type")] public string TokenType { get; set; } }
// 登录 注册 失败时返回错误信息 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 实现 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 }); } }
上面已经做好了基本的结构,接下来就是实现UserService
中的RegisterAsync
和LoginAsync
方法了。这里主要用到identity中的UserManager
,UserManager
封装了很多用户操作的现成方法。
在UserService
中先做一个私有方法,根据user创建jwt token;用户注册,登录成功后调用此方法得到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" }; }
注册方法实现:
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); }
登录方法实现:
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!"}, //用户名或密码错误 }; } return GenerateJwtToken(existingUser); }
最后,别忘了注册UserService
:
services.AddScoped<IUserService, UserService>();
为了方便测试,可以配置一下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"));
随便输入abc进行注册,返回了一些密码规则的错误:
这个规则在注册identity服务时可以配置:
services.AddIdentityCore<AppUser>(options => { options.Password.RequireDigit = true; options.Password.RequireLowercase = false; options.Password.RequireUppercase = false; options.Password.RequireNonAlphanumeric = false; }).AddEntityFrameworkStores<AppDbContext>();
identityOptions
还支持一些其他配置。
下面注册成功后返回了token:
使用刚刚注册的账号测试登录,也没有问题:
本篇完成了identity的登录,注册,获取token,下一篇将介绍如何使用refresh token。
参考:
ASP.NET Core 简介 Identity | Microsoft Docs
Mohamad Lawand - DEV Community