浅谈ASP.NET Core 中jwt授权认证的流程原理 |
|
1,快速实现授权验证 什么是 JWT ?为什么要用 JWT ?JWT 的组成? 这些百度可以直接找到,这里不再赘述 。 实际上,只需要知道 JWT 认证模式是使用一段 Token 作为认证依据的手段 。 我们看一下 Postman 设置 Token 的位置 。
那么,如何使用 C# 的 HttpClient 访问一个 JWT 认证的 WebAPI 呢?
下面来创建一个 ASP.NET Core 项目,尝试添加 JWT 验证功能 。 1.1 添加 JWT 服务配置 在 Startup.cs 的
// 设置验证方式为 Bearer Token
// 你也可以添加 using Microsoft.AspNetCore.Authentication.JwtBearer;
// 使用 JwtBearerDefaults.AuthenticationScheme 代替 字符串 "Brearer"
services.AddAuthentication("Bearer")
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("abcdABCD1234abcdABCD1234")), // 加密解密Token的密钥
// 是否验证发布者
ValidateIssuer = true,
// 发布者名称
ValidIssuer = "server",
// 是否验证订阅者
// 订阅者名称
ValidateAudience = true,
ValidAudience = "client007",
// 是否验证令牌有效期
ValidateLifetime = true,
// 每次颁发令牌,令牌有效时间
ClockSkew = TimeSpan.FromMinutes(120)
};
});
修改 app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); // 注意这里 app.UseAuthorization(); 就是这么简单,通过以上设置,要求验证请求是否有权限 。 1.2 颁发 Token 颁发的 Token ,ASP.NET Core 不会保存 。 ASP.NET Core 启用了 Token 认证,你随便将生成 Token 的代码放到不同程序的控制台,只要密钥和 Issuer 和 Audience 一致,生成的 Token 就可以登录这个 ASP.NET Core 。 也就是说,可以随意创建控制台程序生成 Token,生成的 Token 完全可以登录 ASP.NET Core 程序 。 至于原因,我们后面再说, 在 Program.cs 中,添加一个这样的方法
static void ConsoleToke()
{
// 定义用户信息
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, "痴者工良"),
new Claim(JwtRegisteredClaimNames.Email, "66666666666@qq.com"),
};
// 和 Startup 中的配置一致
SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("abcdABCD1234abcdABCD1234"));
JwtSecurityToken token = new JwtSecurityToken(
issuer: "server",
audience: "client007",
claims: claims,
notBefore: DateTime.Now,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
);
string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
Console.WriteLine(jwtToken);
}
public static void Main(string[] args)
{
ConsoleToke();
CreateHostBuilder(args).Build().Run();
}
1.3 添加 API访问 我们添加一个 API 。
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
public string Get()
{
Console.WriteLine(User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Name));
return "访问成功";
}
}
然后启动 ASP.NET Core,在 Postman 测试 访问 https://localhost/api/home 。
发现报 401 (无权限)状态码,这是因为请求时不携带令牌,会导致不能访问 API 。 从控制台终端复制生成的 Token 码,复制到 Postman 中,再次访问,发现响应状态码为 200,响应成功 。
ASP.NET Core 自带 jwt 认证大概就是这样 。 那么,ASP.NET Core 内部是如何实现的呢?又有哪些特性哪些坑呢?请往下看' 2,探究授权认证中间件 在上面的操作中,我们在管道配置了两个中间件 。 app.UseAuthentication(); app.UseAuthorization();
使用特性设置相应通过认证才能访问,一般有以下情况 。
// 不适用特性,可以直接访问
public class AController : ControllerBase
{
public string Get() { return "666"; }
}
/// <summary>
/// 整个控制器都需要授权才能访问
/// </summary>
[Authorize]
public class BController : ControllerBase
{
public string Get() { return "666"; }
}
public class CController : ControllerBase
{
// 只有 Get 需要授权
[Authorize]
public string Get() { return "666"; }
public string GetB() { return "666"; }
}
/// <summary>
/// 整个控制器都需要授权,但 Get 不需要
/// </summary>
[Authorize]
public class DController : ControllerBase
{
[AllowAnonymous]
public string Get() { return "666"; }
}
2.1 实现 Token 解析 至于 ASP.NET Core 中, 解析出的 Token 是一个 ClaimsPrincipal 对象,将此对象给 在中间件中,使用下面的代码可以获取客户端请求的 Token 解析 。 context.RequestServices.GetRequiredService<IAuthenticationService>().AuthenticateAsync(context, JwtBearerDefaults.AuthenticationScheme); 那么,我们如何手工从原生的 Http 请求中,解析出来呢?且看我慢慢来分解步骤 。 首先创建一个 TestMiddleware 文件,作为中间件使用 。
public class TestMiddleware
{
private readonly RequestDelegate _next;
jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
public TestMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// 我们写代码的区域
// 我们写代码的区域
await _next(context);
}
}
2.1.1 从 Http 中获取 Token 下面代码可以中 http 请求中,取得头部的 Token 。 当然,客户端可能没有携带 Token,可能获取结果为 null ,自己加个判断 。 贴到代码区域 。 string tokenStr = context.Request.Headers["Authorization"].ToString(); Header 的 Authorization 键,是由 2.1.2 判断是否为有效令牌 拿到 Token 后,还需要判断这个 Token 是否有效 。 因为 Authorization 是由
/// <summary>
/// Token是否是符合要求的标准 Json Web 令牌
/// </summary>
/// <param name="tokenStr"></param>
/// <returns></returns>
public bool IsCanReadToken(ref string tokenStr)
{
if (string.IsNullOrWhiteSpace(tokenStr) || tokenStr.Length < 7)
return false;
if (!tokenStr.Substring(0, 6).Equals(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme))
return false;
tokenStr = tokenStr.Substring(7);
bool isCan = jwtSecurityTokenHandler.CanReadToken(tokenStr);
return isCan;
}
获得 Token 后,通过 将下面判断贴到代码区域 。
if (!IsCanReadToken(ref tokenStr))
return ;
2.1.3 解析 Token 下面代码可以将 Header 的 Authorization 内容转为 JwtSecurityToken 对象 。 (截取字符串的方式很多种,喜欢哪个就哪个 。 。 。)
/// <summary>
/// 从Token解密出JwtSecurityToken,JwtSecurityToken : SecurityToken
/// </summary>
/// <param name="tokenStr"></param>
/// <returns></returns>
public JwtSecurityToken GetJwtSecurityToken(string tokenStr)
{
var jwt = jwtSecurityTokenHandler.ReadJwtToken(tokenStr);
return jwt;
}
不过这个
将下面代码贴到代码区域 JwtSecurityToken jst = GetJwtSecurityToken(tokenStr); IEnumerable<Claim> claims = jst.Claims; 2.1.4 生成 context.User context.User 是一个 ClaimsPrincipal 类型,我们通过解析出的 Claim,生成 ClaimsPrincipal 。
JwtSecurityToken jst = GetJwtSecurityToken(tokenStr);
IEnumerable<Claim> claims = jst.Claims;
List<ClaimsIdentity> ci = new List<ClaimsIdentity>() { new ClaimsIdentity(claims) };
context.User = new ClaimsPrincipal(ci);
最终的代码块是这样的
// 我们写代码的区域
string tokenStr = context.Request.Headers["Authorization"].ToString();
string requestUrl = context.Request.Path.Value;
if (!IsCanReadToken(ref tokenStr))
return;
JwtSecurityToken jst = GetJwtSecurityToken(tokenStr);
IEnumerable<Claim> claims = jst.Claims;
List<ClaimsIdentity> ci = new List<ClaimsIdentity>() { new ClaimsIdentity(claims) };
context.User = new ClaimsPrincipal(ci);
var x = new ClaimsPrincipal(ci);
// 我们写代码的区域
2.2 实现校验认证
继续使用上面的中间件,在原代码块区域添加新的区域 。 // 我们写代码的区域 // 我们写的代码块 22.2.1 Endpoint Endpoint 标识了一个 http 请求所访问的路由信息和 Controller 、Action 及其特性等信息 。
以下代码可以获取所访问的节点信息 。 var endpoint = context.GetEndpoint(); 那么如何判断所访问的 Controller 和 Action 是否使用了认证相关的特性? var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>(); Metadata 是一个 ASP.NET Core 实现的集合对象, 这个集合不会区分是 Contrller 还是 Action 的 那么判断 是否有
if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null)
{
await _next(context);
return;
}
到此这篇关于浅谈ASP.NET Core 中jwt授权认证的流程原理的文章就介绍到这了,更多相关ASP.NET Core jwt授权认证内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! |