AspNetCore&JWT认证授权
目录
有时想快速搭建一个简单应用,并集成登录功能时,总是会被认证授权绕来绕去,一直想着要搞个授权中心,却把最为简单快捷的方式抛掷脑后。
认证与授权说来说去还是四个核心步骤,登录退出,登录有效后请求资源,请求人是谁与请求人有没有权限请求。

JWT
JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。其本身只是一种格式或是协议,集成到框架中,然后便按照这种格式或协议来传递信息。
当使用认证授权时,将具有用户信息的令牌以JWT格式的呈现,命名为Id token或是Access token。
项目准备
准备一个Asp.Net Core 6.0的WebApi(前端实现不考虑)。按照如上几个用例挨个实现(退出用例不考虑)

安装Nuget包
<ItemGroup>
  <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
  <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.3" />
  <PackageReference Include="IdentityModel" Version="6.0.0" />
</ItemGroup>
生成JWT格式令牌
增加Account控制器
[ApiController]
[Route("[controller]")]
public class AccountController : ControllerBase
{
}
增加JwtOption
该部分信息用户Jwt格式中所需要的
public class JwtOptions
{
    public const string Name = "Jwt";
    public string Audience { get; set; }
    public string Issuer { get; set; }
    public double ExpiresMinutes { get; set; } = 30d;
    public Encoding Encoding { get; set; } = Encoding.UTF8;
    public string SymmetricSecurityKeyString { get; set; }
    public SymmetricSecurityKey SymmetricSecurityKey => new(Encoding.GetBytes(SymmetricSecurityKeyString));
}
服务注册
builder.Services.Configure<AuthConfigOptions>(builder.Configuration.GetSection(AuthConfigOptions.Name));
配置信息
appsettings.json中增加该块配置
{
  "Jwt": {
    "Audience": "http://localhost:5105",
    "Issuer": "http://localhost:5105",
    "ExpiresMinutes": 30,
    "SymmetricSecurityKeyString": "Symmetric Security Key"
  }
}
注入Option
[ApiController]
[Route("[controller]")]
public class AccountController : ControllerBase
{
    private readonly JwtOptions _jwtOptions;
    public AccountController(IOptionsSnapshot<JwtOptions> jwtOptions)
    {
        _jwtOptions = jwtOptions.Value;
    }
}
增加SignIn方法
此处只模拟存在一个用户,将该用户通过Jwt格式存储信息并颁发token。
[AllowAnonymous]
[HttpPost("Login")]
public IActionResult SignIn([FromBody] SignInDto dto)
{
    //db query...
    //return Unauthorized();
    //user info
    var user = new UserModel()
    {
        Id = Guid.NewGuid(),
        UserName = dto.UserName,
        Email = "test@test.com"
    };
    // 1 定义需要的Cliam信息
    var claims = new[]
    {
        new Claim(JwtClaimTypes.Id, user.Id.ToString("N")),
        new Claim(JwtClaimTypes.Name, user.UserName),
        new Claim(JwtClaimTypes.Email, user.Email)
    };
    // 2 设置SecretKey
    var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOptions.SymmetricSecurityKeyString));
    // 3 设置加密算法
    var algorithm = SecurityAlgorithms.HmacSha256;
    // 4 生成签名凭证信息
    var signingCredentials = new SigningCredentials(secretKey, algorithm);
    // 5 设置token过期时间
    var expires = DateTime.Now.AddMinutes(_jwtOptions.ExpiresMinutes);
    // 6 生成token
    var securityToken = new JwtSecurityToken(
        claims: claims,
        issuer: _jwtOptions.Issuer,
        audience: _jwtOptions.Audience,
        notBefore: DateTime.Now,
        expires: expires,
        signingCredentials: signingCredentials
    );
    var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
    var token = jwtSecurityTokenHandler.WriteToken(securityToken);
    return Ok(new { token });
}
生成token

请求资源
默认模板生成时自带了一个WeatherForecast控制器,此处将其作为资源,对其添加Authorize特性,控制资源。
[ApiController]
[Route("[controller]")]
[Authorize]
public class WeatherForecastController : ControllerBase
{
  
}
因模板中管道部分默认带上了UseAuthorization,因此再次请求WeatherForecast的方法则会报错,没有为Authorization配置相关服务。

增加服务配置
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.RequireHttpsMetadata = false;
        options.SaveToken = true;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = false,
            ValidIssuer = jwtOptions.Issuer,
            ValidateAudience = false,
            ValidAudience = jwtOptions.Audience,
            ValidateLifetime = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.SymmetricSecurityKeyString)),
        };
    });
增加Authentication中间件
app.UseAuthentication();
访问资源

Authorization中间件与Filter区别
在控制器/方法上加Authorize特性,有相应的Filter处理是否有权限,为什么存在了一个Authorization中间件去提前验证?
答:Filter的处理属于MVC的职责范围,而Authorization则是中间件的职责范围,可以认为是总闸与分闸。

2022-04-17,望技术有成后能回来看见自己的脚步。