在NET8中使用简化的 AddJwtBearer 认证

  • 在NET8中使用简化的 AddJwtBearer 认证已关闭评论
  • 84 次浏览
  • A+
所属分类:.NET技术
摘要

系统版本: win10
.NET SDK: NET8
开发工具:vscode
参考引用:使用 dotnet user-jwts 管理开发中的 JSON Web 令牌
注意:以下示例中的端口、token等需替换成你的环境中的信息


开发环境

系统版本: 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件事:

  1. 更新项目的 appsettings.Development.json,添加了Authentication节点
  2. 更新项目的 MyJWT.csproj,添加了UserSecretsId 配置
  3. 创建了机密文件 %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.csprojappsettings.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