ASP.NET Core 中jwt授权认证的流程原理

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介:

ASP.NET Core 中jwt授权认证的流程原理
目录

1,快速实现授权验证
1.1 添加 JWT 服务配置
1.2 颁发 Token
1.3 添加 API访问
2,探究授权认证中间件
2.1 实现 Token 解析
2.2 实现校验认证
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(); 中的功能。

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

        // 我们写代码的区域

        // 我们写的代码块 2

2.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 可以找出需要的特性信息。

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

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

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

原文地址https://www.cnblogs.com/whuanle/p/12497614.html

相关文章
|
3月前
|
JSON 安全 数据安全/隐私保护
Python认证新风尚:OAuth遇上JWT,安全界的时尚Icon👗
【10月更文挑战第2天】在当今互联网世界中,数据安全与隐私保护日益重要。Python 作为广泛应用于 Web 开发的语言,其认证机制也不断进化。OAuth 2.0 和 JSON Web Tokens (JWT) 成为当前最热门的安全认证方案,不仅保障数据安全传输,还简化了用户认证流程。本文将介绍 Python 如何结合 OAuth 2.0 和 JWT 打造安全高效的认证体系。
44 3
|
2月前
|
JSON 安全 算法
Spring Boot 应用如何实现 JWT 认证?
Spring Boot 应用如何实现 JWT 认证?
81 8
|
2月前
|
JSON 安全 数据安全/隐私保护
Python认证新风尚:OAuth遇上JWT,安全界的时尚Icon👗
在当今互联网世界中,数据安全和隐私保护至关重要。Python 作为 Web 开发的主流语言,其认证机制也在不断进步。OAuth 2.0 和 JSON Web Tokens (JWT) 是当前最热门的安全认证方案,不仅保障数据安全传输,还简化用户认证流程。本文介绍如何在 Python 中结合 OAuth 2.0 和 JWT,打造一套既安全又高效的认证体系。通过 Flask-HTTPAuth 和 PyJWT 等库,实现授权和验证功能,确保每次请求的安全性和便捷性。
48 3
|
2月前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
130 2
|
3月前
|
存储 开发框架 .NET
浅析.NET6中的await原理
浅析.NET6中的await原理
57 1
|
4月前
|
安全 Java 数据安全/隐私保护
|
4月前
|
JSON 安全 数据安全/隐私保护
从0到1搭建权限管理系统系列三 .net8 JWT创建Token并使用
【9月更文挑战第22天】在.NET 8中,从零开始搭建权限管理系统并使用JWT(JSON Web Tokens)创建Token是关键步骤。JWT是一种开放标准(RFC 7519),用于安全传输信息,由头部、载荷和签名三部分组成。首先需安装`Microsoft.AspNetCore.Authentication.JwtBearer`包,并在`Program.cs`中配置JWT服务。接着,创建一个静态方法`GenerateToken`生成包含用户名和角色的Token。最后,在控制器中使用`[Authorize]`属性验证和解析Token,从而实现身份验证和授权功能。
286 3
|
Web App开发 .NET
ASP“.NET研究”.NET中的认证与授权
  用户认证   .net提供了3种用户认证的方式,分别是Windows,Forms,Passport。这几种形式的定义可以在网站根目录下Web.config中的authentication节点中看见。
1211 0
|
4月前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
51 7
|
4月前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
86 0