浅谈ASP.NET Core 中jwt授权认证的流程原理(2)

// 不适用特性,可以直接访问 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 中,app.UseAuthentication(); 和 app.UseAuthorization(); 的源代码各种使用了一个项目来写,代码比较多。要理解这两个中间件的作用,我们不妨来手动实现他们的功能。

解析出的 Token 是一个 ClaimsPrincipal 对象,将此对象给 context.User 赋值,然后在 API 中可以使用 User 实例来获取用户的信息。

在中间件中,使用下面的代码可以获取客户端请求的 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 键,是由 Breaer {Token}组成的字符串。

2.1.2 判断是否为有效令牌

拿到 Token 后,还需要判断这个 Token 是否有效。

因为 Authorization 是由 Breaer {Token}组成,所以我们需要去掉前面的 Brear 才能获取 Token。

/// <summary> /// Token是否是符合要求的标准 Json Web 令牌 /// </summary> /// <param></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 后,通过 JwtSecurityTokenHandler.CanReadToken(tokenStr); 来判断 Token 是否符合协议规范。

将下面判断贴到代码区域。

if (!IsCanReadToken(ref tokenStr)) return ;

2.1.3 解析 Token

下面代码可以将 Header 的 Authorization 内容转为 JwtSecurityToken 对象。

(截取字符串的方式很多种,喜欢哪个就哪个。。。)

/// <summary> /// 从Token解密出JwtSecurityToken,JwtSecurityToken : SecurityToken /// </summary> /// <param></param> /// <returns></returns> public JwtSecurityToken GetJwtSecurityToken(string tokenStr) { var jwt = jwtSecurityTokenHandler.ReadJwtToken(tokenStr); return jwt; }

不过这个 GetJwtSecurityToken 不是我们关注的内容,我们是要获取 Claim。

JwtSecurityToken.Claims

将下面代码贴到代码区域

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 实现校验认证

app.UseAuthentication(); 的大概实现过程已经做出了说明,现在我们来继续实现 app.UseAuthorization(); 中的功能。

继续使用上面的中间件,在原代码块区域添加新的区域。

// 我们写代码的区域 // 我们写的代码块

22.2.1 Endpoint

Endpoint 标识了一个 http 请求所访问的路由信息和 Controller 、Action 及其特性等信息。

[Authorize] 特性继承了 IAuthorizeData。[AllowAnonymous] 特性继承了 IAllowAnonymous。

以下代码可以获取所访问的节点信息。

var endpoint = context.GetEndpoint();

那么如何判断所访问的 Controller 和 Action 是否使用了认证相关的特性?

var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();

Metadata 是一个 ASP.NET Core 实现的集合对象,GetOrderedMetadata<T> 可以找出需要的特性信息。

这个集合不会区分是 Contrller 还是 Action 的 [Authorize] 特性。

那么判断 是否有 [AllowAnonymous] 特性,可以这样使用。

if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null) { await _next(context); return; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wydzfw.html