- A+
所属分类:.NET技术
开发环境
系统版本: win10
.NET SDK: NET8
开发工具:vscode
参考引用:使用 dotnet user-jwts 管理开发中的 JSON Web 令牌
注意:以下示例中的端口、token等需替换成你的环境中的信息
创建项目
运行以下命令来创建一个空的 Web 项目,并添加 Microsoft.AspNetCore.Authentication.JwtBearer NuGet 包:
dotnet new web -o MyJWT cd MyJWT dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
将 Program.cs 的内容替换为以下代码(略微改动):
using System.Security.Claims; var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthorization(); // 默认的Scheme是Bearer // builder.Services.AddAuthentication("Bearer").AddJwtBearer(); builder.Services.AddAuthentication().AddJwtBearer(); var app = builder.Build(); app.UseAuthorization(); app.MapGet("/", () => "Hello, World!"); app.MapGet("/secret", (ClaimsPrincipal user) => $"Hello {user.Identity?.Name}. My secret") .RequireAuthorization(); app.Run();
运行项目并访问接口返回以下内容
PS D:LearnMyJWT> curl.exe -i http:///localhost:5276 HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Date: Mon, 04 Dec 2023 00:43:03 GMT Server: Kestrel Transfer-Encoding: chunked Hello, World!
创建 JWT
PS D:LearnMyJWT> dotnet user-jwts create New JWT saved with ID 'c28b968'. Name: Lingpeng Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IkxpbmdwZW5nIiwic3ViIjoiTGluZ3BlbmciLCJqdGkiOiJjMjhiOTY4IiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6Mjk3NTQiLCJodHRwczovL2xvY2FsaG9zdDo0NDM2MCIsImh0dHA6Ly9sb2NhbGhvc3Q6NTI3NiIsImh0dHBzOi8vbG9jYWxob3N0OjcyNTMiXSwibmJmIjoxNzAxNjQ5Nzk2LCJleHAiOjE3MDk1MTIxOTYsImlhdCI6MTcwMTY0OTc5NiwiaXNzIjoiZG90bmV0LXVzZXItand0cyJ9.l52s9_7oNjIKL96TysgdE0k970fUS9FoLTu2xRs-IPo
这个命令做了3件事:
- 更新项目的
appsettings.Development.json
,添加了Authentication节点 - 更新项目的
MyJWT.csproj
,添加了UserSecretsId 配置 - 创建了机密文件
%APPDATA%MicrosoftUserSecrets<secrets_GUID>user-jwts.json
与%APPDATA%MicrosoftUserSecrets<secrets_GUID>secrets.json
,机密管理参考
我们看下这两个机密文件
user-jwts.json
{ "c28b968": { "Id": "c28b968", "Scheme": "Bearer", "Name": "Lingpeng", "Audience": "http://localhost:29754, https://localhost:44360, http://localhost:5276, https://localhost:7253", "NotBefore": "2023-12-04T00:29:56+00:00", "Expires": "2024-03-04T00:29:56+00:00", "Issued": "2023-12-04T00:29:56+00:00", "Token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IkxpbmdwZW5nIiwic3ViIjoiTGluZ3BlbmciLCJqdGkiOiJjMjhiOTY4IiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6Mjk3NTQiLCJodHRwczovL2xvY2FsaG9zdDo0NDM2MCIsImh0dHA6Ly9sb2NhbGhvc3Q6NTI3NiIsImh0dHBzOi8vbG9jYWxob3N0OjcyNTMiXSwibmJmIjoxNzAxNjQ5Nzk2LCJleHAiOjE3MDk1MTIxOTYsImlhdCI6MTcwMTY0OTc5NiwiaXNzIjoiZG90bmV0LXVzZXItand0cyJ9.l52s9_7oNjIKL96TysgdE0k970fUS9FoLTu2xRs-IPo", "Scopes": [], "Roles": [], "CustomClaims": {} } }
secrets.json
{ "Authentication:Schemes:Bearer:SigningKeys": [ { "Id": "ff20683d", "Issuer": "dotnet-user-jwts", "Value": "lDOFmIuEDelFKU0zAaLoT2qYOFDRZGDDTv5FyTa36V8=", "Length": 32 } ] }
测试JWT
我们重新运行程序,用直接访问与携带token两种方式访问/secret接口
PS D:LearnMyJWT> curl.exe -i http://localhost:5276/secret HTTP/1.1 401 Unauthorized Content-Length: 0 Date: Mon, 04 Dec 2023 00:43:25 GMT Server: Kestrel WWW-Authenticate: Bearer PS D:LearnMyJWT> PS D:LearnMyJWT> PS D:LearnMyJWT> curl.exe -i -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IkxpbmdwZW5nIiwic3ViIjoiTGluZ3BlbmciLCJqdGkiOiJjMjhiOTY4IiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6Mjk3NTQiLCJodHRwczovL2xvY2FsaG9zdDo0NDM2MCIsImh0dHA6Ly9sb2NhbGhvc3Q6NTI3NiIsImh0dHBzOi8vbG9jYWxob3N0OjcyNTMiXSwibmJmIjoxNzAxNjQ5Nzk2LCJleHAiOjE3MDk1MTIxOTYsImlhdCI6MTcwMTY0OTc5NiwiaXNzIjoiZG90bmV0LXVzZXItand0cyJ9.l52s9_7oNjIKL96TysgdE0k970fUS9FoLTu2xRs-IPo" http://localhost:5276/secret HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Date: Mon, 04 Dec 2023 00:45:42 GMT Server: Kestrel Transfer-Encoding: chunked Hello Lingpeng. My secret
至此我们已经实现了JwtBearer的初步使用
一点点改动
示例采用了机密管理,我们也可以把机密文件中的内容迁移至项目中(推荐用机密管理),我们修改MyJWT.csproj
与appsettings.Development.json
如下
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <!-- <UserSecretsId>88d7c163-def1-4747-b01f-cefed382beae</UserSecretsId> --> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" /> </ItemGroup> </Project>
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "Authentication": { "Schemes": { "Bearer": { "ValidAudiences": [ "http://localhost:29754", "https://localhost:44360", "http://localhost:5276", "https://localhost:7253" ], "ValidIssuer": "dotnet-user-jwts", "SigningKeys": [ { "Id": "ff20683d", "Issuer": "dotnet-user-jwts", "Value": "lDOFmIuEDelFKU0zAaLoT2qYOFDRZGDDTv5FyTa36V8=", "Length": 32 } ] } } } }
修改完成后实现相同的功能
JWT Token生成示例
app.MapGet("/login", (string UserName, string Password, [FromServices] IOptionsMonitor<JwtBearerOptions> optionsMonitor) => { // 1. 密码验证 // TODO // 2. 生成 var parameters = optionsMonitor.Get(JwtBearerDefaults.AuthenticationScheme).TokenValidationParameters; var signingKey = parameters.IssuerSigningKeys.First(); var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256Signature); var header = new JwtHeader(signingCredentials); var payload = new JwtPayload { { JwtRegisteredClaimNames.UniqueName, UserName }, { JwtRegisteredClaimNames.Iss, parameters.ValidIssuers.First() }, { JwtRegisteredClaimNames.Aud, parameters.ValidAudiences }, { JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds() }, { JwtRegisteredClaimNames.Nbf, DateTimeOffset.UtcNow.ToUnixTimeSeconds() }, { JwtRegisteredClaimNames.Exp, DateTimeOffset.UtcNow.AddMinutes(30).ToUnixTimeSeconds() } }; var jwtSecurityToken = new JwtSecurityToken(header, payload); var jwtSecurityTokenHandler = new JwtSecurityTokenHandler(); var token = jwtSecurityTokenHandler.WriteToken(jwtSecurityToken); return token; });
进行一下验证
PS D:LearnMyJWT> curl.exe -i "http://localhost:5276/login?username=admin&password=1111" HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Date: Mon, 04 Dec 2023 05:03:36 GMT Server: Kestrel Transfer-Encoding: chunked eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImFkbWluIiwiaXNzIjoiZG90bmV0LXVzZXItand0cyIsImF1ZCI6WyJodHRwOi8vbG9jYWxob3N0OjI5NzU0IiwiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzNjAiLCJodHRwOi8vbG9jYWxob3N0OjUyNzYiLCJodHRwczovL2xvY2FsaG9zdDo3MjUzIl0sImlhdCI6MTcwMTY2NjIxNiwibmJmIjoxNzAxNjY2MjE2LCJleHAiOjE3MDE2NjgwMTZ9.P9t7vIFfM7cddRPs4OQUTVVdo57nWTLt_ea2UynGUpo PS D:LearnMyJWT> PS D:LearnMyJWT> PS D:LearnMyJWT> curl.exe -i -H "Authorization: Bearer eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImFkbWluIiwiaXNzIjoiZG90bmV0LXVzZXItand0cyIsImF1ZCI6WyJodHRwOi8vbG9jYWxob3N0OjI5NzU0IiwiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzNjAiLCJodHRwOi8vbG9jYWxob3N0OjUyNzYiLCJodHRwczovL2xvY2FsaG9zdDo3MjUzIl0sImlhdCI6MTcwMTY2NjIxNiwibmJmIjoxNzAxNjY2MjE2LCJleHAiOjE3MDE2NjgwMTZ9.P9t7vIFfM7cddRPs4OQUTVVdo57nWTLt_ea2UynGUpo" http://localhost:5276/secret HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Date: Mon, 04 Dec 2023 05:03:50 GMT Server: Kestrel Transfer-Encoding: chunked Hello admin. My secret