.NET 8 集成 JWT Bearer Token

注意:这是一种非常简单且不是最低限度安全的设置 JWT 的方法。

步骤 1——安装软件包

首先,您需要安装一些 NuGet 包。

dotnet add package Microsoft.AspCore.Authentication.JwtBearer
dotnet add package System.IdentityModel.Tokens.Jwt

步骤 2——创建类

你需要创建一些类来处理登录请求、注册请求和授权响应。你还需要某种类型的用户。

using System.ComponentModel.DataAnnotations;

namespace Auth;

public class LoginRequest
{
    [Required]
    [EmailAddress]
    public string Email { get; set; }

    [Required]
    public string Password { get; set; }
}

using System.ComponentModel.DataAnnotations;

namespace Auth;

public class RegistrationRequest
{
    [Required]
    public string UserID { get; set; }

    [Required]
    public string Username { get; set; }

    [Required]
    public string Firstname { get; set; }

    [Required]
    public string Lastname { get; set; }

    [Required]
    [EmailAddress]
    public string Email { get; set; }

    [Required]
    public string Password { get; set; }
}

namespace Auth;

public class AuthResponse
{
    public string UserId { get; set; }
    public string Username { get; set; }
    public string Token { get; set; }
    public string? ProfileImage { get; set; }
}

步骤 3——创建控制器

当我们创建了类之后,我们需要实现注册用户和登录的逻辑。我将跳过创建用户的逻辑,只在我的示例中使用我的服务,您需要自己做这件事。

[HttpPost("register")]
public async Task<IActionResult> Register([FromBody] RegistrationRequest request)
{
    var salt = GenerateSalt();
    var saltedPassword = request.Password + salt;

    var user = new User
    {
        Firstname = request.Firstname,
        Lastname = request.Lastname,
        Email = request.Email,
        Password = _passwordHasher.HashPassword(null, saltedPassword),    // Null is because the user is not created yet, normally this is where the user object is.
        Salt = salt,
        Role = Enums.Role.USER
    };

    await _userService.CreateUser(user);
    var token = _tokenService.CreateToken(user);

    return Ok(new AuthResponse { Token = token });
}

[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginRequest request)
{
    User? user = await _userService.FindByEmailAsync(request.Email);        

    if (user == null)
    {
        return Unauthorized("Invalid credentials 1");
    }

    var saltedPassword = request.Password + user.Salt;
    
    var result = _passwordHasher.VerifyHashedPassword(user, user.Password,saltedPassword);

    if (result != PasswordVerificationResult.Success)
    {
        return Unauthorized("Invalid credentials 2");
    }

    // Generate token
    var token = _tokenService.CreateToken(user);

    // Return the token
    return Ok(new AuthResponse { Token = token });
}

步骤4 —创建令牌服务

现在我们需要创建一个生成 JWT 令牌的方法。在我们的令牌中,我们可以添加任意数量的索赔正如我们想要的那样。这里需要注意的是,我们的 SecurityTokenDescriptor 必须具有与“Program.cs”中的授权设置相同的令牌验证参数。我们稍后会再讨论这一点。我在这里使用的名为“_configuration”的变量,正是通过“IConfiguration”依赖注入到我的控制器中的。

public string CreateToken(User user)
{
    var tokenHandler = new JwtSecurityTokenHandler();
    var key = Encoding.ASCII.GetBytes(_configuration["Jwt:Key"]);
    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(new Claim[]
        {
            new Claim(ClaimTypes.NameIdentifier, user.UserID),
            new Claim(ClaimTypes.Role, user.Role.ToString()),
            // Add more claims as needed
        }),
        Expires = DateTime.UtcNow.AddHours(1),
        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
        Issuer = _configuration["Jwt:Issuer"], // Add this line
        Audience = _configuration["Jwt:Audience"] 
    };

    var token = tokenHandler.CreateToken(tokenDescriptor);
    return tokenHandler.WriteToken(token);
}

步骤 5 — 设置 Swagger 配置

现在我们需要配置 Swagger,以便稍后测试我们的端点。这里需要注意的是,当我们在 Swagger 的授权输入框中输入令牌字符串时,需要依次输入:Bearer,后跟一个空格,然后是令牌字符串。

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new() { Title = "Pappa´s API", Version = "v1" });

    // Define the OAuth2.0 scheme that's in use (i.e., Implicit Flow)
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey,
        Scheme = "Bearer"
    });

    c.AddSecurityRequirement(new OpenApiSecurityRequirement()
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                },
                Scheme = "oauth2",
                Name = "Bearer",
                In = ParameterLocation.Header,
            },
            new List<string>()
        }
    });
});

步骤 6——设置授权

这里重要的是,我们的令牌验证参数与我们在令牌服务中的“CreateToken”方法中添加的参数相同。

builder.Services.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"])),
            ClockSkew = TimeSpan.Zero
        };
    });

步骤 7——设置中间件

我们放置中间件的顺序非常重要,所以不要弄错顺序。我们也指定 Swagger 仅在开发环境中有效。

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();

步骤 8 — 设置应用程序设置

现在我们需要将 JWT 值添加到我们的“apptsettings.json”文件中,包括密钥、颁发者和受众。出于演示和开发目的,我仅使用这些简单的值。但请注意,密钥尤其不应以纯文本形式存储,为了安全起见,最好将所有值存储在安全的地方,例如 Azure Secrets 或 GitHub Secrets。

"Jwt": {
    "Key": "your_secret_key_here_your_secret_key_here",
    "Issuer": "your_issuer",
    "Audience": "your_audience"
  }

步骤 9 — 在控制器中设置授权

现在是时候将我们的令牌集成到我们的某个 API 中了。为此,我们让 .NET 处理编码,就像我们在“Program.cs”文件中设置的那样。我们还添加了“[Authroize]”和“Roles”,以实现基于角色的身份验证。如果您想添加更多角色,只需在同一字符串中用逗号分隔它们即可。

[HttpGet("getuser")]
    [Authorize(Roles = "USER")]
    public async Task<ActionResult<User>> GetUser()
    {
        // Retrieve userId from the claims
        var userIdClaim = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
        
        Console.WriteLine("Claims received:");
        foreach (var claim in User.Claims)
        {
            Console.WriteLine($"{claim.Type}: {claim.Value}");
        }

        if(userIdClaim == null)
        {
            return Unauthorized("No user ID claim present in token.");
        }
        
        try
        {
            User? user = await _userService.GetUser(userIdClaim);
            return Ok(user);
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(ex.Message);
        }
    }
    
关于.NET8集成JWT本文先介绍到这里了。

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

资源下载链接为: https://pan.xunlei.com/s/VOYpbLrm0qylrALOE1le-EJFA1?pwd=x564 在 B 站有一则标题为《DeepSeek-v3 扩展树莓派,让语音助手给你 “唱歌”,秒变智能音箱》的视频,视频链接为https://www.bilibili.com/video/BV1a3cceEEpP/?sharesource=copyweb&vd_source=245c190fe77b507d57968a57b3d6f9cf,发布于 2025 年 1 月 11 日 16:18:06。 该视频截至相关数据统计时,播放量达 7229 次,弹幕有 4 条,获得 123 个点赞、98 枚硬币,被 182 人收藏,转发次数为 32 次。视频作者是 “bi 胜 li 量老师”,作者认为 AI 与人的互动在持续进步,AI 不仅能理解并回应人类语言,还可提供个性化服务与帮助。 此外,还有不少相关视频,比如教大家不丢弃废弃音箱,将其升级为已接入 DeepSeek 的小智智能音箱;展示在树莓派上部署小智 AI 的成果,不过存在一点小问题,就是语音助手话太多,使用者插不上话;呈现小爱同学智能音箱与 DeepSeek 的完美结合,打造树莓派 AI 小智;提供资料,指导大家打造树莓派私人语音助手,实现年轻人拥有第一个 “贾维斯” 的想法;还有原创内容,介绍基于国内语音方案的树莓派语音助手,从千问 3 到 DeepSeek-R1-0528,可直连大模型对话;以及教大家用树莓派打造智能语音音箱,该音箱具备聊天、控制家电等高级功能,还有在树莓派 4B 上体验 DeepSeek R1 推理大模型的相关内容。
<think>好的,我现在需要在.NET 8 Web API中实现JWT Token的刷新机制。首先,我需要了解JWT的基本结构和工作流程。JWT通常分为三个部分:头部、载荷和签名。在认证过程中,用户登录后服务器生成一个访问令牌(access token)和一个刷新令牌(refresh token)。访问令牌有效期较短,而刷新令牌有效期较长,用于获取新的访问令牌。 接下来,我需要确定如何存储和管理刷新令牌。常见的方法是将刷新令牌存储在数据库或缓存中,这样可以在每次刷新请求时验证其有效性。不过,在分布式系统中,可能需要使用分布式缓存来确保一致性。 然后,我需要考虑如何生成新的访问令牌。当客户端使用刷新令牌请求新的访问令牌时,服务器需要验证刷新令牌的有效性,检查是否过期或被撤销。如果有效,生成新的访问令牌,并可能生成新的刷新令牌(取决于是否需要刷新令牌的轮换)。 还需要设计API端点来处理刷新请求。通常,这会是一个单独的端点,比如`/api/auth/refresh`,接受刷新令牌并返回新的访问令牌和可能的新的刷新令牌。 在实现过程中,需要注意安全性。例如,刷新令牌必须安全地传输和存储,防止泄露。同时,需要考虑令牌的撤销机制,比如在用户注销时使相关的刷新令牌失效。 另外,参考现有的实现,比如使用`System.IdentityModel.Tokens.Jwt`库来生成和验证令牌,结合ASP.NET Core的中间件进行认证和授权。可能需要自定义中间件或使用现有的包来处理刷新逻辑。 最后,测试整个流程,确保访问令牌过期后,客户端能使用刷新令牌获取新的访问令牌,而无需重新登录。同时,处理各种异常情况,如无效的刷新令牌、过期的刷新令牌等,返回适当的HTTP状态码和错误信息。 总结步骤: 1. 用户登录生成访问令牌和刷新令牌。 2. 存储刷新令牌(数据库/缓存)。 3. 创建刷新令牌端点,验证刷新令牌并生成新令牌。 4. 处理令牌撤销和安全性问题。 5. 测试和异常处理。 现在,根据这些步骤,可以编写具体的代码示例,配置JWT服务,添加刷新端点,并实现令牌刷新逻辑。</think>在 .NET 8 Web API 中实现 JWT 令牌刷新机制需要以下关键步骤: --- ### 一、核心实现步骤 #### 1. 配置 JWT 服务 在 `Program.cs` 中添加 JWT 认证服务,并设置访问令牌和刷新令牌的过期时间: ```csharp builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = "your_issuer", ValidateAudience = true, ValidAudience = "your_audience", ValidateLifetime = true, // 验证令牌有效期 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your_secure_key_32_bytes_long")), ClockSkew = TimeSpan.Zero // 严格校验过期时间 }; }); ``` #### 2. 生成双令牌(访问令牌 + 刷新令牌) ```csharp public (string AccessToken, string RefreshToken) GenerateTokens(string userId) { var accessToken = new JwtSecurityToken( issuer: "your_issuer", audience: "your_audience", claims: new[] { new Claim(ClaimTypes.NameIdentifier, userId) }, expires: DateTime.UtcNow.AddMinutes(15), // 短效访问令牌 signingCredentials: new SigningCredentials( new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your_secure_key")), SecurityAlgorithms.HmacSha256) ); var refreshToken = new JwtSecurityToken( issuer: "your_issuer", audience: "your_audience", claims: new[] { new Claim(ClaimTypes.NameIdentifier, userId) }, expires: DateTime.UtcNow.AddDays(7), // 长效刷新令牌 signingCredentials: new SigningCredentials( new SymmetricSecurityKey(Encoding.UTF8.GetBytes("refresh_token_secret")), SecurityAlgorithms.HmacSha256) ); return ( new JwtSecurityTokenHandler().WriteToken(accessToken), new JwtSecurityTokenHandler().WriteToken(refreshToken) ); } ``` #### 3. 存储刷新令牌 使用分布式缓存(如 Redis)存储刷新令牌与用户关联: ```csharp // 存储示例(使用IDistributedCache) await cache.SetStringAsync($"refresh_token:{userId}", refreshToken, new DistributedCacheEntryOptions { AbsoluteExpiration = DateTimeOffset.UtcNow.AddDays(7) }); ``` #### 4. 创建刷新端点 添加 `/api/auth/refresh` 端点处理刷新请求: ```csharp app.MapPost("/api/auth/refresh", async (RefreshRequest request, ICacheService cache) => { var principal = ValidateRefreshToken(request.RefreshToken); var userId = principal.FindFirstValue(ClaimTypes.NameIdentifier); // 验证缓存中的刷新令牌是否匹配 var storedToken = await cache.GetStringAsync($"refresh_token:{userId}"); if (storedToken != request.RefreshToken) { return Results.Unauthorized(); } var newTokens = GenerateTokens(userId); await cache.SetStringAsync($"refresh_token:{userId}", newTokens.RefreshToken); // 更新刷新令牌 return Results.Ok(new { newTokens.AccessToken, newTokens.RefreshToken }); }); ``` --- ### 二、安全增强措施 1. **刷新令牌轮换** 每次刷新后生成新的刷新令牌,使旧令牌立即失效,防止重复使用[^2]。 2. **HTTPS 强制要求** 所有令牌传输必须通过 HTTPS 加密。 3. **令牌黑名单** 实现令牌撤销机制,将失效的访问令牌加入短期黑名单(如内存缓存)。 --- ### 三、完整请求流程示例 1. **用户登录** 返回 `access_token`(15分钟有效)和 `refresh_token`(7天有效) ```http POST /api/auth/login Response: { "accessToken": "...", "refreshToken": "..." } ``` 2. **访问令牌过期** 客户端收到 `401 Unauthorized` 错误时,使用刷新令牌请求新令牌: ```http POST /api/auth/refresh Body: { "refreshToken": "..." } Response: { "accessToken": "...(新的)", "refreshToken": "...(新的)" } ``` 3. **主动注销** 删除缓存中的刷新令牌,使所有关联令牌失效: ```csharp await cache.RemoveAsync($"refresh_token:{userId}"); ``` --- ### 四、引用说明 [^1]: 使用 `System.IdentityModel.Tokens.Jwt` 可确保标准的 JWT 生成/验证流程。 [^2]: 通过中间件校验令牌可统一处理认证逻辑,减少代码冗余。 [^3]: Bearer Token 模式是 JWT 在 HTTP Header 中的标准传递方式。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

csdn_aspnet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值