浅谈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 的 ConfigureServices 方法中,添加一个服务

  // 设置验证方式为 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)
     };
    });

修改 Configure 中的中间件

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

Main() 中,调用此方法

 public static void Main(string[] args)
  {
   ConsoleToke();
   CreateHostBuilder(args).Build().Run();
  }

1.3 添加 API访问

我们添加一个 API。

[Authorize] 特性用于标识此 Controller 或 Action 需要使用合规的 Token 才能登录。

 [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();

app.UseAuthentication(); 的作用是通过 ASP.NET Core 中配置的授权认证,读取客户端中的身份标识(Cookie,Token等)并解析出来,存储到 context.User 中。

app.UseAuthorization(); 的作用是判断当前访问 Endpoint (Controller或Action)是否使用了 [Authorize]以及配置角色或策略,然后校验 Cookie 或 Token 是否有效。

使用特性设置相应通过认证才能访问,一般有以下情况。

 // 不适用特性,可以直接访问
 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 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 后,通过 JwtSecurityTokenHandler.CanReadToken(tokenStr); 来判断 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;
  }

不过这个 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;
   }

到此这篇关于浅谈ASP.NET Core 中jwt授权认证的流程原理的文章就介绍到这了,更多相关ASP.NET Core jwt授权认证内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • asp.net core集成JWT的步骤记录

    [什么是JWT] JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案. JWT的官网地址:https://jwt.io/ 通俗地来讲,JWT是能代表用户身份的令牌,可以使用JWT令牌在api接口中校验用户的身份以确认用户是否有访问api的权限. JWT中包含了身份认证必须的参数以及用户自定义的参数,JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名. [什么时候应该使用JSON Web令牌?] 授权:这是使用JWT的最常见方案.一旦用户登录

  • ASP.Net Core3.0中使用JWT认证的实现

    JWT认证简单介绍 关于Jwt的介绍网上很多,此处不在赘述,我们主要看看jwt的结构. JWT主要由三部分组成,如下: HEADER.PAYLOAD.SIGNATURE HEADER 包含token的元数据,主要是加密算法,和签名的类型,如下面的信息,说明了 加密的对象类型是JWT,加密算法是HMAC SHA-256 {"alg":"HS256","typ":"JWT"} 然后需要通过BASE64编码后存入token中 eyJ

  • Asp.Net Core基于JWT认证的数据接口网关实例代码

    前言 近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对前后端分离的数据服务支持,于是想到我一直做.Net开发,问我是否对.Net Core有所了解?能不能做个简单Demo出来看看?我说,分道扬镳之后我不是调用别人的接口就是提供接口给别人调用,于是便有了以下示例代码. 示例要求能演示获取Token及如何使用该Token访问数据资源,在Demo中实现

  • 浅谈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 的 Confi

  • 浅谈ASP.NET Core静态文件处理源码探究

    前言 静态文件(如 HTML.CSS.图像和 JavaScript)等是Web程序的重要组成部分.传统的ASP.NET项目一般都是部署在IIS上,IIS是一个功能非常强大的服务器平台,可以直接处理接收到的静态文件处理而不需要经过应用程序池处理,所以很多情况下对于静态文件的处理程序本身是无感知的.ASP.NET Core则不同,作为Server的Kestrel服务是宿主到程序上的,由宿主运行程序启动Server然后可以监听请求,所以通过程序我们直接可以处理静态文件相关.静态文件默认存储到项目的ww

  • 浅谈ASP.NET Core的几种托管方式

    Kestrel Kestrel 是一个跨平台的适用于 ASP.NET Core 的 Web 服务器,默认包括在 ASP.NET Core 项目模板中. Kestrel 支持以下方案: HTTPS 用于启用 WebSocket 的不透明升级 用于获得 Nginx 高性能的 Unix 套接字 HTTP/2(除 macOS† 以外) 可以单独使用 Kestrel,也可以将其与反向代理服务器 (如 Internet Information Services (IIS).Nginx 或 Apache)结合

  • 浅谈ASP.NET Core中间件实现分布式 Session

    1.1. 中间件原理 1.1.1. 什么是中间件 中间件是段代码用于处理请求和响应,通常多个中间件链接起来形成管道,由每个中间件自己来决定是否要调用下一个中间件. 1.1.2. 中间件执行过程 举一个示例来演示中间件的执行过程(分别有三个中间件:日志记录.权限验证和路由):当请求进入应用程序时,执行执行日志记录的中间件,它记录请求属性并调用链中的下一个中间件权限验证,如果权限验证通过则将控制权传递给下一个中间件,不通过则设置401 HTTP代码并返回响应,响应传递给日志中间件进行返回. 1.1.

  • 浅谈ASP.NET Core 2.0 带初始参数的中间件(译)

    问题 如何在ASP.NET Core 2.0向中间件传入初始参数? 答案 在一个空项目中,创建一个POCO(Plain Old CLR Object)来保存中间件所需的参数: public class GreetingOptions { public string GreetAt { get; set; } public string GreetTo { get; set; } } 添加一个中间件: public class GreetingMiddleware { private readon

  • 浅谈ASP.NET Core 2.0 布局页面(译)

    本文介绍了ASP.NET Core 2.0 布局页面,分享给大家,具体如下: 问题 如何在ASP.NET Core 2.0项目中共享可见元素.代码块和指令? 答案 新建一个空项目,首先添加GreetingService服务和UserViewModel模型: public interface IGreetingService { string Greet(string firstname, string surname); } public class GreetingService : IGre

  • 浅谈ASP.NET Core 2.0 中间件(译)

    问题 如何创建一个最简单的ASP.NET Core中间件? 答案 使用VS创建一个ASP.NET Core 2.0的空项目,注意Startup.cs中的Configure()方法: public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.Run(async (context) => { await context.Response.WriteAsync("Hello World! (Run)

  • 浅谈ASP.Net Core WebApi几种版本控制对比

    一.版本控制的好处: (1)有助于及时推出功能, 而不会破坏现有系统. (2)它还可以帮助为选定的客户提供额外的功能. API 版本控制可以采用不同的方式进行控制,方法如下: (1)在 URL 中追加版本或作为查询字符串参数, (2)通过自定义标头和通过接受标头 在这篇文章中, 让我们来看看如何支持多个版本的 ASP.NET Core Web API. 一.创建asp.net core webapi 项目,引用NuGet包:Install-Package Microsoft.AspNetCore

  • 浅谈ASP.NET Core 中间件详解及项目实战

    前言 本篇文章是我们在开发自己的项目中实际使用的,比较贴合实际应用,算是对中间件的一个深入使用了,不是简单的Hello World. 中间件(Middleware)的作用 我们知道,任何的一个web框架都是把http请求封装成一个管道,每一次的请求都是经过管道的一系列操作,最终到达我们写的代码中.那么中间件就是在应用程序管道中的一个组件,用来拦截请求过程进行一些其他处理和响应.中间件可以有很多个,每一个中间件都可以对管道中的请求进行拦截,它可以决定是否将请求转移给下一个中间件. asp.net

  • 浅谈ASP.NET Core 2.0 部分视图(译)

    问题 如何在ASP.NET Core 2.0中使用部分视图来重用页面的公共部分? 答案 新建一个空项目,在Startup中添加MVC服务和中间件: public void ConfigureServices(IServiceCollection services) { services.AddMvc(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopmen

随机推荐