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算法)或公共和私有RSA或ECDSA。
¿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 SHA256或RSA。
例如:
{
"alg": "HS256",
"typ": "JWT"
}
然后,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.
- 例如,我们声称要保护 ASP.NET Core 的角色,其名称是:
Un ejemplo de un Payload Sería el siguiente:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
这是一个相同的标头,是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)
您可以使用红色验证工具来验证您的身份,并使用带有私有证书的令牌来验证您的发射器。
Juntando todo
最后,我们将在 Base64-URL 中提供文本编码的趋势,以解决问题中的 HTTP 或 HTML 问题。与使用 XML 的 SAML 相比,它的形式更加紧凑。
最后,tendríamos un JWT de la siguiente forma:
如果您需要使用 JWT,请访问jwt.io。
JWT 是如何工作的?
如果您需要进行身份验证,请使用 JSON Web Token 来使用凭证。如果你想使用我们的授权,你应该考虑一下你是否需要保护令牌,如果你没有任何要求,那么很快就会消除这种情况。
您可以使用常规协议来限制连接,包括令牌、HTTP 标头授权和使用 esquema Bearer。
例如:
Authorization: Bearer <token>
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 中的授权和验证的示意图:
- 应用客户端请求授权身份服务器(例如 Auth0 或 Azure AD B2C)。这是在OpenID Connect上定义的授权定义的不同之处(但无需继续执行)。从形式上看,OpenID 是
/oauth/authorize通过代码流使用端点的提示。 - 授权访问权限,授权服务器将访问令牌注册到应用程序客户端
- 使用访问令牌来访问递归程序的客户端应用程序(例如 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 是由一些控制器、控制器、Carter、ApiEndpoints或所需的更多端点组成的。
Para comenzar, crearemos un proyecto 网络空间:
dotnet new web -o WebApiJwt
需要注册以下文件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>
持久性
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)
{
}
}
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!;
}
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>();
AddSqlite: 登记习惯DbContext性方法AddDbContextAddIdentityCore: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 claseIdentityRole)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"]))
};
});
AddHttpContextAccessor:Registra elIHttpContextAccessorque nos Permite acceder elHttpContextde 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 TokensAddJwtBearer:配置令牌的验证,特别是验证和使用隐私的具体信息- 当然,您可以通过查看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": "*"
}
在此情况下,需要创建数据库迁移(例如,SQLite)并实现身份预定义的任务:
dotnet ef migrations add FirstMigration -o Persistence/Migrations
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();
终点
实施端点、验证和模拟访问限制
授权端点(/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
});
});
各方之间的划分:
- 凭证验证:使用 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);
受保护的端点(/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();
使用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");
}
}
简单地说,就是存在数据的基础,并且预先没有任何用途,可以创建角色并使用身份类别。
角色可以使用自动端点来使用。这是一个单独的问题,包括在 JWT 中与 ASP.NET 解决问题。
// ...Más código
var app = builder.Build();
await SeedData();
app.UseAuthentication();
app.UseAuthorization();
// Más código...
使用VS Code 的 HTTP Rest 的应用程序和基本操作(适用于 Postman 或客户的 HTTP 需求):
请求:
POST {{host}}/token
Content-Type: application/json
{
"userName": "test.demo",
"password": "P@ss.W0rd"
}
回复:
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"
}
请注意使用或反对不正确的情况。
验证端点 protegido llamamos 端点/me:
GET {{host}}/me
Content-Type: application/json
Authorization: Bearer {{jwt}}
回复:
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"
}
您可以修改JWT.io 的代币手册或修改数据并探索相关信息。
结论
JSON Web Tokens 可以转换为现代应用程序的默认认证。为此,我们必须配备专门的网络应用程序。
使用 asp.net Identity 是一种推荐的机制(或验证机制),它是一种安全和对比的方式,它是我们关注的重点和使用框架企业的准备,可以在重新发明的过程中使用。
参考资料
在 ASP.NET Core 5 中实现 JWT 身份验证 (codemag.com)
文章来源:https://dev.to/isaacojeda/aspnet-core-6-autenticacion-jwt-y-identity-core-170i


