.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
    });
});

 

 

  • 配置身份认证服务
  1. Configure方法

app.UseAuthorization()之前增加app.UseAuthentication();

  1. 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数据库
  1. 引入依赖
<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>

 

  1. ConfigureServices方法
var sqlConnection = Configuration.GetConnectionString("SqlServerConnection");
services.AddDbContext<WebAPIDBContent>(option => option.UseSqlServer(sqlConnection));

 

  • 配置数据库
  1. 创建表

 

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; }
}

 

  1. 创建数据库
public class WebAPIDBContent : DbContext
{
    public WebAPIDBContent(DbContextOptions<WebAPIDBContent> options) : base(options)
    {
    }

    public DbSet<User> Users { get; set; }
}
  1. .迁移数据库

数据库连接配置: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();
}
  • 测试
  1. 注册一个管理员账户

  1. 登录,获取token

  1. 点击右上角的Authorize填写token

  1. 成功调用其他API

 调用Get/Users API 时,如果未登录,返回401,如果登录的不是管理员账户(用户类型Admin),返回403

 项目地址:https://gitee.com/ALittleDruid/WebAPIServer

posted @ 2021-03-11 08:20  山上一边边  阅读(925)  评论(0编辑  收藏  举报