发布于 2026-01-06 3 阅读
0

ASP.NET Core 6:JWT 身份验证和身份核心简介 JWT 身份验证承载是什么? ASP.NET 和最小 API 的探针结论参考

ASP.NET Core 6:JWT 身份验证和身份核心

引言

JWT 认证持有者

¿Y el codigo? Probemos 与 ASP.NET 和最小 API

结论

参考资料

引言

本文探讨了 JSON Web 令牌的特性、组合和实现,利用最小 API 和 ASP.NET 身份。

该代码是在github 上的存储库中遇到的。 Espero les sea de utilidad。

JWT 认证持有者

什么是 JSON Web Token?

JSON Web Token (JWT) 是标准 ( RFC 7519 ),它定义了以 JSON 对象形式传输信息的安全性和紧凑性。

我们已提供可验证的信息并可与数字化公司进行确认。 JWT 已公开使用私有(包含HMAC算法)或公共和私有RSAECDSA

¿Cuando deberías utilizar Json Web Tokens?

以下是推荐使用 JWT 的场景:

  • 授权:可以与 JWT 共同使用。如果您开始使用,则可以使用包括 JWT 在内的后续服务,并允许使用常规服务或递归方式来单独使用 debido 令牌。 SSO(单点登录)是 JWT 的一项功能,可以简化操作并提高不同国家的使用能力。
  • Intercambio de Información: Los JWT 可以在实体间传输信息。德比多是智威汤逊公司的主要成员 - 例如,使用公共/私人 - 可以确保你的信息安全。另外,还可以使用 JWT 的扩展和负载(有效负载)进行计算,以便确保 JWT 的连接不会发生变化。

¿JWT 的结构是什么?

Un JWT está separado por puntos ( . )en tres partses, las cuales son:

  • Encabezado(标题)
  • 内容(有效载荷)
  • Firma(签名)

Un JWT comúnmente tiene la siguiente forma。

xxxxx.yyyyy.zzzzz

Veamos quesignifica cada una de estas partses。

标题

该功能多个部分组成:令牌(例如 JWT)和算法可以在固件中使用,并且可以使用HMAC SHA256RSA

例如:

{
  "alg": "HS256",
  "typ": "JWT"
}
Enter fullscreen mode Exit fullscreen mode

然后,JSON 编码为Base64URL格式,包含 JWT 引言部分的格式。

有效载荷

JWT 的第二部分将继续传输证书(有效负载),并继续索赔系列。声明内容为确认(usualmente, el usuario)和附加信息。索赔的类型:注册人、公共机构和私人机构。

  • 声明注册:与预先定义的声明相结合,没有任何推荐的义务,以及与声明互操作性相结合的证明。 Algunos de ellos son: iss (发行人), exp (tiempo de expiración), sub (主题), aud (受众), entre otros

💡 注意,声明是关于 JWT de tamaño reducido 的错误意图的第三封信。

  • 公共声明: Estos pueden ser definidos como cada quien desee、pero para evitar colisiones de nombres y mantener un estándar (ya que puede usarse en distintos servicios)、se utiliza la siguiente lista llamada IANA JSON Web Token Registration
  • 隐私声明: Estos 声明其个人化是基于 JWT 和公共系统的个性化,避免冲突,并可使用 URL 格式和名称空间以及作为公共服务进行推荐
    • 例如,我们声称要保护 ASP.NET Core 的角色,其名称是:http://schemas.microsoft.com/ws/2008/06/identity/claims/role.

Un ejemplo de un Payload Sería el siguiente:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}
Enter fullscreen mode Exit fullscreen mode

这是一个相同的标头,是Base64Url 中的代码段。

💡注意:JWT 是固定的,单独的 protegidos para evitar falsificaciones(编辑有效负载)pero de igual forma,toda la información en el Payload esvisible para cualquiera。有效负载中不包含任何明智的信息* *

签名

创建头部编码、有效载荷编码、秘密、头部和待办事项的具体算法。

例如,如果使用 HMAC SHA256 加密算法,则可以创建以下格式的固件:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Enter fullscreen mode Exit fullscreen mode

您可以使用红色验证工具来验证您的身份,并使用带有私有证书的令牌来验证您的发射器。

Juntando todo

最后,我们将在 Base64-URL 中提供文本编码的趋势,以解决问题中的 HTTP 或 HTML 问题。与使用 XML 的 SAML 相比,它的形式更加紧凑。

最后,tendríamos un JWT de la siguiente forma:

JWT

如果您需要使用 JWT,请访问jwt.io。

JWT 是如何工作的?

如果您需要进行身份验证,请使用 JSON Web Token 来使用凭证。如果你想使用我们的授权,你应该考虑一下你是否需要保护令牌,如果你没有任何要求,那么很快就会消除这种情况。

您可以使用常规协议来限制连接,包括令牌、HTTP 标头授权和使用 esquema Bearer。

例如:

Authorization: Bearer <token>
Enter fullscreen mode Exit fullscreen mode

Web API 的一般性(以及更多)是无状态应用程序,需要使用令牌Authorization。服务验证是必要的,因为令牌有效或不有效。了解信息(索赔)和使用需求。

您可以减少对常用信息的数据咨询,也可以使用令牌内容来操作操作者的信息(例如用户名、电子邮件、角色等)。

Dado que el token va incluido en el header, no habrá Problemas con el Cross-Origin ResourceSharing (CORS) ya que no se utilizan cookies (las cookies son por dominio).

使用 JWT 中的授权和验证的示意图:

图片描述

  1. 应用客户端请求授权身份服务器(例如 Auth0 或 Azure AD B2C)。这是在OpenID Connect上定义的授权定义的不同之处(但无需继续执行)。从形式上看,OpenID 是/oauth/authorize通过代码流使用端点的提示。
  2. 授权访问权限,授权服务器将访问令牌注册到应用程序客户端
  3. 使用访问令牌来访问递归程序的客户端应用程序(例如 una API)

¿Y el codigo? Probemos 与 ASP.NET 和最小 API

这就是使用生产准备就绪的方法和简单的操作方法,并决定如何构建和实施。

前面提到的 OpenId,特别是实现了授权的方式,通过实践和教学,实现了授权服务(API 协议的服务),同时也提供了值得推荐的服务流程(como Auth0) o 框架 (como IdentityServer) 认证以确保合规性。

En este proyecto utilizaremos:

  • Entity Framework Core 与 SQLite 持久化(与 SQL Azure 或类似服务一起使用的产品、服务)
  • ASP.NET Identity 是一个凭证管理工具。
  • 最小的 API 是由一些控制器、控制器、CarterApiEndpoints或所需的更多端点组成的。

Para comenzar, crearemos un proyecto 网络空间:

dotnet new web -o WebApiJwt
Enter fullscreen mode Exit fullscreen mode

需要注册以下文件WebApiJwt.csproj

<ItemGroup>
  <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.1" />
  <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
  <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.1" />
  <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.1" />
  <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1" />
  <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.1">
    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    <PrivateAssets>all</PrivateAssets>
  </PackageReference>
</ItemGroup>
Enter fullscreen mode Exit fullscreen mode

持久性

Crearemos una carpeta llamada “Persistence” y aquí pondremos las migraciones y el DbContextcon tablas preestablecidas por Identity:

using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using WebApiJwt.Entities;

namespace WebApiJwt.Persistence;

public class MyDbContext : IdentityDbContext<User>
{
    public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
    {
    }
}
Enter fullscreen mode Exit fullscreen mode

Para lo cual, necesitaremos nuestra definición custom de la clase Usuario:

using Microsoft.AspNetCore.Identity;

namespace WebApiJwt.Entities;

public class User : IdentityUser
{
    public string FirstName { get; set; } = default!;
    public string LastName { get; set; } = default!;
}
Enter fullscreen mode Exit fullscreen mode

Aquí estamos usando un DbContextcon tablas preestablecidas y IdentityUseresparte de ellas, alone lo estamos Extendiendo para agregar Campos individualizados (nombre y apellidos)。

身份和 JWT 配置

配置 Identity 和 EntityFramework,在新存档Program.cs中注册 siguientes :

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using WebApiJwt.Entities;
using WebApiJwt.Models;
using WebApiJwt.Persistence;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddSqlite<MyDbContext>(builder.Configuration.GetConnectionString("Default"))
    .AddIdentityCore<User>()
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<MyDbContext>();
Enter fullscreen mode Exit fullscreen mode
  • AddSqlite: 登记习惯DbContext性方法AddDbContext
  • AddIdentityCore:Registra las dependentencias que necesita Identity、como Genrador de contraseñas、manejo de usuarios 等
  • AddRoles:Registra todo lo necesario para poder usar Roles(en este caso, con la Implementación default de la clase IdentityRole
  • AddEntityFrameworkStores: EntityFramework 的上下文与我们所依赖的一样,身份需要尊重持久性

需要注意的是,JWT 中所需的配置如下:

builder.Services
    .AddHttpContextAccessor()
    .AddAuthorization()
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
        };
    });
Enter fullscreen mode Exit fullscreen mode
  • AddHttpContextAccessor:Registra el IHttpContextAccessorque nos Permite acceder el HttpContextde cada solicitud (la usaremos más adelante para acceder al usuarioactual autenticado)
  • AddAutorization: Dependencias necesarias para autorizar solicitudes (como autorización por 角色)
  • AddAuthentication: Agrega el esquema de autenticación que queramos usar, en este caso, queremos usar por default la autenticación por Bearer Tokens
  • AddJwtBearer:配置令牌的验证,特别是验证和使用隐私的具体信息
    • 当然,您可以通过查看appsettings.json进行配置

重要信息配置档案:

{
  "ConnectionStrings": {
    "Default": "Data Source=Identity.db"
  },
  "Jwt": {
    "Issuer": "WebApiJwt.com",
    "Audience": "localhost",
    "Key": "S3cr3t_K3y!.123_S3cr3t_K3y!.123"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}
Enter fullscreen mode Exit fullscreen mode

在此情况下,需要创建数据库迁移(例如,SQLite)并实现身份预定义的任务:

dotnet ef migrations add FirstMigration -o Persistence/Migrations
Enter fullscreen mode Exit fullscreen mode

Y contaríamos con algo 类似于 lo siguiente:

图片描述

完成配置和验证实施前的准备工作后,中间件将自动解码 JWT 和 Agregarlo(en caso de ser válido)以请求 HTTP。

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/", () => "Hello World!");

app.Run();
Enter fullscreen mode Exit fullscreen mode

终点

实施端点、验证和模拟访问限制

授权端点(/token):

app.MapPost("/token", async (AuthenticateRequest request, UserManager<User> userManager) =>
{
    // Verificamos credenciales con Identity
    var user = await userManager.FindByNameAsync(request.UserName);

    if (user is null || !await userManager.CheckPasswordAsync(user, request.Password))
    {
        return Results.Forbid();
    }

    var roles = await userManager.GetRolesAsync(user);

    // Generamos un token según los claims
    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.Sid, user.Id),
        new Claim(ClaimTypes.Name, user.UserName),
        new Claim(ClaimTypes.GivenName, $"{user.FirstName} {user.LastName}")
    };

    foreach (var role in roles)
    {
        claims.Add(new Claim(ClaimTypes.Role, role));
    }

    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]));
    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
    var tokenDescriptor = new JwtSecurityToken(
        issuer: builder.Configuration["Jwt:Issuer"],
        audience: builder.Configuration["Jwt:Audience"],
        claims: claims,
        expires: DateTime.Now.AddMinutes(720),
        signingCredentials: credentials);

    var jwt = new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);

    return Results.Ok(new
    {
        AccessToken = jwt
    });
});
Enter fullscreen mode Exit fullscreen mode

各方之间的划分:

  • 凭证验证:使用 ASP.NET 的身份验证来保护用户(最多可用于单独用户的功能)和角色。UserManager与常用的方法、对立和角色相关的方法很多。
  • JWT 的生成:使用 JWT 生成的声明列表。这是一个样板文件,很简单。重要的是应用程序设置配置的使用,以及 JWT 所关心的问题。

Por parametro se recibe el usuario y contraseña, este es el siguiente record:

namespace WebApiJwt.Models;
public record AuthenticateRequest(string UserName, string Password);
Enter fullscreen mode Exit fullscreen mode

受保护的端点(/me)

Este 端点 lo único que hará es regresar la información del usuario (claims) según el JWT que se mandó:

app.MapGet("/me", (IHttpContextAccessor contextAccessor) =>
{
    var user = contextAccessor.HttpContext.User;

    return Results.Ok(new
    {
        Claims = user.Claims.Select(s => new
        {
            s.Type,
            s.Value
        }).ToList(),
        user.Identity.Name,
        user.Identity.IsAuthenticated,
        user.Identity.AuthenticationType
    });
})
.RequireAuthorization();
Enter fullscreen mode Exit fullscreen mode

使用IHttpContextAccessor中间件自动解码并简单地获取有关信息。

需要注意的是,对于特定端点的扩展RequireAuthorization是必要的,但如果相反,则需要使用默认值,即不记名令牌。

尝试解决方案

可能的情况、使用的必要性、创建Program.csSeedData方法

async Task SeedData()
{
    var scopeFactory = app!.Services.GetRequiredService<IServiceScopeFactory>();
    using var scope = scopeFactory.CreateScope();

    var context = scope.ServiceProvider.GetRequiredService<MyDbContext>();
    var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();
    var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
    var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();

    context.Database.EnsureCreated();

    if (!userManager.Users.Any())
    {
        logger.LogInformation("Creando usuario de prueba");

        var newUser = new User
        {
            Email = "test@demo.com",
            FirstName = "Test",
            LastName = "User",
            UserName = "test.demo"
        };

        await userManager.CreateAsync(newUser, "P@ss.W0rd");
        await roleManager.CreateAsync(new IdentityRole
        {
            Name = "Admin"
        });
        await roleManager.CreateAsync(new IdentityRole
        {
            Name = "AnotherRole"
        });

        await userManager.AddToRoleAsync(newUser, "Admin");
        await userManager.AddToRoleAsync(newUser, "AnotherRole");
    }
}
Enter fullscreen mode Exit fullscreen mode

简单地说,就是存在数据的基础,并且预先没有任何用途,可以创建角色并使用身份类别。

角色可以使用自动端点来使用。这是一个单独的问题,包括在 JWT 中与 ASP.NET 解决问题。

// ...Más código

var app = builder.Build();

await SeedData();

app.UseAuthentication();
app.UseAuthorization();

// Más código...
Enter fullscreen mode Exit fullscreen mode

使用VS Code 的 HTTP Rest 的应用程序和基本操作(适用于 Postman 或客户的 HTTP 需求):

请求:

POST {{host}}/token
Content-Type: application/json

{
    "userName": "test.demo",
    "password": "P@ss.W0rd"
}
Enter fullscreen mode Exit fullscreen mode

回复:

HTTP/1.1 200 OK
Connection: close
Content-Type: application/json; charset=utf-8
Date: Sun, 02 Jan 2022 22:32:30 GMT
Server: Kestrel
Transfer-Encoding: chunked

{
  "accessToken": "eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9zaWQiOiJhMWNhODMxZC1iMTIzLTQ0ZDgtYjViOC1iNjNlYWZiYzZlNDciLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoidGVzdC5kZW1vIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZ2l2ZW5uYW1lIjoiVGVzdCBVc2VyIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjpbIkFub3RoZXJSb2xlIiwiQWRtaW4iXSwiZXhwIjoxNjQxMjA1OTUwLCJpc3MiOiJXZWJBcGlKd3QuY29tIiwiYXVkIjoibG9jYWxob3N0In0.CtTkO7JVmFl6ASRv1v7OuZhCrOHUy-AiMfNUzQbYByc"
}
Enter fullscreen mode Exit fullscreen mode

请注意使用或反对不正确的情况。

验证端点 protegido llamamos 端点/me

GET {{host}}/me
Content-Type: application/json
Authorization: Bearer {{jwt}}
Enter fullscreen mode Exit fullscreen mode

回复:

HTTP/1.1 200 OK
Connection: close
Content-Type: application/json; charset=utf-8
Date: Sun, 02 Jan 2022 22:33:56 GMT
Server: Kestrel
Transfer-Encoding: chunked

{
  "claims": [
    {
      "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid",
      "value": "a1ca831d-b123-44d8-b5b8-b63eafbc6e47"
    },
    {
      "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
      "value": "test.demo"
    },
    {
      "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname",
      "value": "Test User"
    },
    {
      "type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
      "value": "AnotherRole"
    },
    {
      "type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
      "value": "Admin"
    },
    {
      "type": "exp",
      "value": "1641205950"
    },
    {
      "type": "iss",
      "value": "WebApiJwt.com"
    },
    {
      "type": "aud",
      "value": "localhost"
    }
  ],
  "name": "test.demo",
  "isAuthenticated": true,
  "authenticationType": "AuthenticationTypes.Federation"
}
Enter fullscreen mode Exit fullscreen mode

您可以修改JWT.io 的代币手册或修改数据并探索相关信息。

结论

JSON Web Tokens 可以转换为现代应用程序的默认认证。为此,我们必须配备专门的网络应用程序。

使用 asp.net Identity 是一种推荐的机制(或验证机制),它是一种安全和对比的方式,它是我们关注的重点和使用框架企业的准备,可以在重新发明的过程中使用。

参考资料

JSON Web Token 简介 - jwt.io

在 ASP.NET Core 5 中实现 JWT 身份验证 (codemag.com)

文章来源:https://dev.to/isaacojeda/aspnet-core-6-autenticacion-jwt-y-identity-core-170i