JWT 原理:
配置 JWT
(1)书写 JWT 配置节点,节点下创建 SecKey, ExpireSeconds 两个配置项,分别代表着 JWT 的密钥和过期时间(我这里配置在了 appsettings.json 中,过期时间单位为 秒,可根据自身情况选择)
(1)创建配置类, 包含 SecKey(密钥)、ExpireSeconds(过期时间) 两个属性(例如 JWTOptions 类,名字等啥的不需要一样),方便我们读取配置
public class JWTOptions
{
public string SecKey { get; set; }
public int ExpireSeconds { get; set; }
}
(2)Nuget:
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
(3)对 JWT 进行注册,注册好后如果报文头中有 Authorization 会自动帮我们解析校验 JWT 令牌,如果访问的 Action 方法有 Authorize 特性,只有校验成功才可以访问
//注册配置,并将配置绑定到对应的实列,让其他地方调用,比如说我这里在生成 JWT 令牌的时候用到
builder.Services.Configure<JWTOptions>(builder.Configuration.GetSection("JWT"));
//注册 JWT
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).
AddJwtBearer(option =>
{
//再在里面做一些 JWT 配置,对于 JWT 的配置
//再将一些 JWT 的数据读取一下,在读取密钥等,校验
JWTOptions jwtOptions = builder.Configuration.GetSection("JWT").Get<JWTOptions>();
byte[] keyBytes = Encoding.UTF8.GetBytes(jwtOptions.SecKey);
var secKey = new SymmetricSecurityKey(keyBytes);
option.TokenValidationParameters = new()
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = secKey
};
});
生成 JWT 令牌
(1)Program.cs 的 app.UseAuthorization() 这行代码之前添加 app.UseAuthentication()
ASP.NET Core 中身份验证和授权验证的功能有 Authentication、Authorization 中间件提供
这一步非常重要,否则 Asp.Net Core 就不会在我们访问添加特性为 [Authorize] 的 Action 方法时帮我们自动校验 JWT 令牌
app.UseAuthentication();//在 app.UseAuthorization 前添加 app.UseAuthentication()
app.UseAuthorization();
(2)生成 JWT 令牌并返回给客户端(具体看开头 JWT 原理)
[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
//设置接口调用配置
private readonly IOptionsSnapshot<JWTOptions> optionsSnapshot;
public ValuesController(IOptionsSnapshot<JWTOptions> optionsSnapshot)
{
this.optionsSnapshot = optionsSnapshot;
}
/// <summary>
/// 获取 JWT 令牌,一般来说,都是先判断有没有传令牌过来,没有的话需要看有没有该用户,有创建令牌返回回去,没有创建用户返回回去,改文章只关注 JWT 这里就不写了
/// 我这里简写
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult<string>> ReturnJWT(string name, string password)
{
string jwt = string.Empty;
if (name == "百香果" && password == "123456")
{
//生成令牌
List<Claim> claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, name));
claims.Add(new Claim(ClaimTypes.Role, "admin"));
//读取配置获取到 key
string key = optionsSnapshot.Value.SecKey;
//设置过期时间,意思是发给你的 jwt 什么时候过期
DateTime expire = DateTime.Now.AddSeconds(optionsSnapshot.Value.ExpireSeconds);//设置过期时间
byte[] secBytes = Encoding.UTF8.GetBytes(key);
var secKey = new SymmetricSecurityKey(secBytes);
var credentials = new SigningCredentials(secKey, SecurityAlgorithms.HmacSha256Signature);
var tokenDescriptor = new JwtSecurityToken(claims: claims,
expires: expire, signingCredentials: credentials);
jwt = new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);
}
return jwt;
}
}
携带 JWT 令牌访问对应 Action 方法
注意:我们不需要自己在书写解析 JWT 令牌的代码,当我们访问 只有登录才能访问的 Action 方法时,之前通过 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).
AddJwtBearer(…) 注册的服务就会帮我们自动校验,如果是对的,就可以访问该 Action 方法,否则反之。
所做的要求就是服务端需要校验 JWT 令牌的 Action 方法上 需要加上 Authorize 特性,客户端需要在报文头中加入 Authorization 的值为 “Bearer JWTToken(JWT 令牌)” 来访问(Authorization 的值中的
“Bearer” 和 JWT 令牌之间一定要通过空格分隔,前后不能多出来额外的空格,换行等),具体如下:
(1)在需要登录才能访问的控制器类或者 Action 方法上添加 [Authorize] (放在控制器上则对该控制器内所有的 Action 方法都生效,否则只对指定的 Action 方法生效,只有登录才能访问这些方法)
(2)再次访问的时候根据登录时传递给客户端的 JWT 令牌 在报文头中定义 Authorization 访问添加了特性 [Authorize] 的Action 方法,然后根据注册进去的服务自动校验 JWT 令牌,校验成功就可以访问该需要登录才可以访问的 Action 方法, 并将明文数据放到 User 中,供我们调用,不添加 [Authorize] 不用登录也可以访问, 只需要知道地址就可以了
Postman 访问:
[Route("api/[controller]/[action]")]
[ApiController]
[Authorize]
public class AuthorizeController : ControllerBase
{
/// <summary>
/// 获取 传过来的 JWT 令牌里登录时传的明文信息
/// </summary>
/// <returns></returns>
[HttpGet]
public string GetInfo()
{
//当传递过来令牌后,获取令牌里登录时传的明文信息,可以通过下面的方法
var name = this.User.FindFirst(ClaimTypes.Name)!.Value;
var roles = this.User.FindAll(ClaimTypes.Role);
string roleStr = string.Join(",", roles.Select(c => c.Value));
return name + "|" + roleStr;
}
/// <summary>
/// 下面方法不需要验证,添加 AllowAnonymous 特性即可
/// </summary>
/// <returns></returns>
[HttpPost]
[AllowAnonymous]
public string GetInfo1()
{
return "111";
}
}