.Net5.0 WebApi 注册登录以及基于JWT的简单身份认证与授权
- 新建一个API的项目
powershell中执行
dotnet new webapi
或者使用Visual Studio 2019
默认的模板已经配置了Swagger中间件,启用Swagger的验证功能,ConfigureServices方法中增加
services.AddSwaggerGen(c => { ... c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Id = "Bearer", Type = ReferenceType.SecurityScheme } }, Array.Empty<string>() } }); c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "Please enter into field the word 'Bearer' followed by a space and the JWT value", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey }); });
- 配置身份认证服务
- Configure方法
app.UseAuthorization()之前增加app.UseAuthentication();
- ConfigureServices方法
增加
services.AddAuthorization(options => { options.AddPolicy(JwtBearerDefaults.AuthenticationScheme, policy => { policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme); policy.RequireClaim(ClaimTypes.Name); policy.RequireRole(ClaimTypes.Role); }); }); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Events = new JwtBearerEvents { OnTokenValidated = async context => { var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>(); var name = context.Principal.Identity.Name; var user = await userService.GetByNameAysnc(name); if (user == null) { context.Fail("Unauthorized"); } } }; options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false, ValidateIssuer = false, ValidateActor = false, ValidateLifetime = true, IssuerSigningKey = UsersController.SecurityKey }; });
- 使用mssql数据库
- 引入依赖
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference>
- ConfigureServices方法
var sqlConnection = Configuration.GetConnectionString("SqlServerConnection"); services.AddDbContext<WebAPIDBContent>(option => option.UseSqlServer(sqlConnection));
- 配置数据库
- 创建表
public class User { [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [Required, MaxLength(64)] public string UserName { get; set; } [Required, MaxLength(64)] public byte[] PasswordHash { get; set; } [Required, MaxLength(128)] public byte[] PasswordSalt { get; set; } [Required, MaxLength(20)] public string Phone { get; set; } [Required, MaxLength(20)] public string UserType { get; set; } }
- 创建数据库
public class WebAPIDBContent : DbContext { public WebAPIDBContent(DbContextOptions<WebAPIDBContent> options) : base(options) { } public DbSet<User> Users { get; set; } }
- .迁移数据库
数据库连接配置:appsettings.json中增加
"ConnectionStrings": { "SqlServerConnection": "Server=(localdb)\\mssqllocaldb;Database=WedAPIServer;Trusted_Connection=True;MultipleActiveResultSets=true" },
程序包管理器控制台中执行
Add-Migration InitialCreate
Update-Database
- 增加用户控制器
[Authorize(Roles = "Admin")] [ApiController] [Route("[controller]")] public class UsersController : ControllerBase { public static JwtSecurityTokenHandler JwtTokenHandler = new JwtSecurityTokenHandler(); public static SymmetricSecurityKey SecurityKey = new SymmetricSecurityKey(Guid.NewGuid().ToByteArray()); private IUserService _userService; private IMapper _mapper; private IActionContextAccessor _accessor; private readonly ILogger<UsersController> _logger; public UsersController( ILogger<UsersController> logger, IUserService userService, IMapper mapper, IActionContextAccessor httpContextAccessor) { _logger = logger; _userService = userService; _mapper = mapper; _accessor = httpContextAccessor; } [AllowAnonymous] [HttpPost("authenticate")] public async Task<IActionResult> Authenticate(UserDto userDto) { _logger.LogInformation($"Authenticate {userDto.Username}@{userDto.Password}"); var user = await _userService.AuthenticateAysnc(userDto.Username, userDto.Password); if (user == null) return BadRequest(new { message = "Username or password is incorrect" }); var claims = new[] { new Claim(ClaimTypes.Name, user.UserName), new Claim(ClaimTypes.Role, user.UserType) }; var credentials = new SigningCredentials(SecurityKey, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken("WebAPIServer", "WebAPIClients", claims, expires: DateTime.Now.AddDays(1), signingCredentials: credentials); var tokenString = JwtTokenHandler.WriteToken(token); return Ok(new { Type = "Bearer", Token = tokenString }); } [AllowAnonymous] [HttpPost("register")] public async Task<IActionResult> Register(UserDto userDto) { _logger.LogInformation($"Register {userDto.Username}@{userDto.Password}@{userDto.Phone}"); var user = _mapper.Map<User>(userDto); try { var _user = await _userService.CreateAysnc(user, userDto.Password); return Ok(_mapper.Map<UserDto>(_user)); } catch (AppException ex) { _logger.LogWarning($"Register AppException"+ ex.Message); return BadRequest(new { message = ex.Message }); } } [HttpGet] public async Task<IActionResult> GetAll() { _logger.LogInformation($"GetAll"); var users = await _userService.GetAllAysnc(); var userDtos = _mapper.Map<List<UserDto>>(users); return Ok(userDtos); } [HttpGet("{id}")] public async Task<IActionResult> GetById(int id) { _logger.LogInformation($"GetById {id}"); var user = await _userService.GetByIdAysnc(id); var userDto = _mapper.Map<UserDto>(user); return Ok(userDto); } [HttpPut("{id}")] public async Task<IActionResult> Update(int id, UserDto userDto) { _logger.LogInformation($"Update {id} {userDto.Username}@{userDto.Password}@{userDto.Phone}"); var user = _mapper.Map<User>(userDto); user.Id = id; try { await _userService.UpdateAysnc(user, userDto.Password); return Ok(); } catch (AppException ex) { return BadRequest(new { message = ex.Message }); } } [HttpDelete("{id}")] public async Task<IActionResult> Delete(int id) { _logger.LogInformation($"Delete {id}"); await _userService.DeleteAysnc(id); return Ok(); }
- 测试
- 注册一个管理员账户
- 登录,获取token
- 点击右上角的Authorize填写token
- 成功调用其他API
调用Get/Users API 时,如果未登录,返回401,如果登录的不是管理员账户(用户类型Admin),返回403