.NET 8 集成 JWT Bearer Token

.NET8集成JWTBearerToken教程

注意:这是一种非常简单且不是最低限度安全的设置 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本文先介绍到这里了。

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

<think> 首先,用户的问题是关于在.NET Core的ABP框架中实现JWT RefreshToken。ABP框架是一个基于ASP.NET Core的开源应用框架,它支持JWT认证。 我需要回顾提供的引用,因为它们包含了关于JWT和RefreshToken的实现细节。引用涉及JWT认证、RefreshToken机制、无感刷新Token等,尽管它们不是针对ABP框架的,但我可以提取通用概念应用到ABP语境中。 关键引用: - [^1]: 讨论了创建JWT配置。 - [^2]: 描述了在登录时生成refreshToken并存储它。 - [^3]: 推荐使用双Token机制(Access Token和Refresh Token)。 - [^4]: 展示了使用中间件刷新Token的示例。 - [^5]: 涉及授权和资源服务器。 ABP框架已经内置了身份验证和授权功能,包括JWT支持。我应该参考ABP的文档或常见实践来实现RefreshToken。 在ABP中实现JWT RefreshToken的步骤: 1. **配置JWT认证**:在ABP中,通常在模块中配置JWT选项。 2. **生成RefreshToken**:在登录逻辑中添加生成refreshToken,并存储它(如数据库或缓存)。 3. **处理Token刷新**:实现一个端点或中间件来使用refreshToken获取新的access token。 4. **安全考虑**:处理refreshToken的过期、撤销等。 ABP提供了模块化的方式,我可以使用ABP的Identity模块或自定义实现。 根据引用[^3],双Token机制是推荐的: - Access Token:短期有效,用于认证API请求。 - Refresh Token:长期有效,用于获取新access token。 在ABP中,可能需要扩展默认的AuthController或登录服务。 实现方法: - **自定义控制器**:添加一个Login方法生成双token。 - **存储RefreshToken**:使用ABP的缓存或数据库存储refreshToken,映射到用户。 - **刷新端点**:创建一个API端点接受refreshToken来返回新access token。 - **中间件**:如引用[^4],使用中间件自动检测并刷新token。 ABP可能有内置支持或扩展点。我应该建议查看ABP文档,但基于一般知识给出指导。 步骤分解: 1. **配置JWT**:在ABP模块中配置JWT认证选项。 2. **登录实现**:在登录方法中,除了access token,还生成一个refreshToken(如GUID),并存储它。 3. **存储RefreshToken**:使用内存缓存(如IMemoryCache)或数据库(ABP有仓储模式)。 4. **刷新Token端点**:实现一个如/token/refresh的API端点,接受refreshToken,验证后返回新access token。 5. **过期处理**:access token过期时间短(如30分钟),refresh token长(如7天)。 安全问题:刷新token时,应撤销旧refreshToken,防止重用。 在ABP中,可以利用依赖注入和模块化。 相关代码示例参考引用: - 从[^2]:修改Login action生成refreshToken并存储。 - 从[^4]:中间件刷新token,但可能需要在ABP中集成。 ABP框架有自己的认证系统;我需要确保兼容。 最终回答应结构化: - 介绍概念。 - 逐步实现方法。 - 代码片段(使用中文注释)。 - 相关问题。 回答语言:中文。 添加引用标识:基于引用,如[^3]提到双Token机制。 相关问题生成:如ABP中RefreshToken的最佳实践,如何处理token撤销等。 </think> ### 在 .NET Core ABP 框架中实现 JWT RefreshToken 的方法 ABP (ASP.NET Boilerplate) 框架是一个基于 .NET Core 的开源应用框架,它提供了模块化和可扩展的架构。ABP 默认支持 JWT (JSON Web Token) 认证,但实现 RefreshToken(刷新令牌)机制需要一些自定义扩展。RefreshToken 用于在 Access Token 过期后获取新 Access Token,避免用户频繁重新登录,从而提高用户体验[^3]。以下步骤基于 ABP 框架的最佳实践和通用 JWT 实现原理,结合引用内容,逐步解释实现方法。 --- #### 步骤 1: **配置 ABP 框架的 JWT 认证** ABP 内置了 JWT 支持,但需要配置认证选项。在您的 ABP 模块(如 `YourProjectHttpApiModule.cs`)中添加或修改 JWT 设置: - **设置 Access Token 和 Refresh Token 过期时间**:Access Token 应为短期(例如 30 分钟),Refresh Token 为长期(例如 7 天)。 - **使用 `Configure<JwtBearerOptions>` 方法**:定义令牌参数。 ```csharp using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.Text; namespace YourProject.Web { [DependsOn(typeof(AbpAspNetCoreAuthenticationJwtBearerModule))] // 确保依赖 JWT 模块 public class YourProjectHttpApiModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { // 配置 JWT 认证 Configure<AbpJwtBearerOptions>(options => { options.SecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your_secret_key_here")); // 替换为实际密钥 options.Issuer = "your_issuer"; options.Audience = "your_audience"; options.Expiration = TimeSpan.FromMinutes(30); // Access Token 过期时间 options.SigningCredentials = new SigningCredentials(options.SecurityKey, SecurityAlgorithms.HmacSha256); }); } } } ``` - **关键点**:ABP 的 `AbpJwtBearerOptions` 简化了 JWT 配置[^1]。确保从 NuGet 安装 `Volo.Abp.AspNetCore.Authentication.JwtBearer` 包(对应 .NET Core 版本,如 .NET 8.0)。 --- #### 步骤 2: **扩展登录逻辑以生成 RefreshToken** 在 ABP 的身份认证服务(如 `AuthController`)中修改登录方法: - **生成双 Token(Access Token + Refresh Token)**:登录时创建 RefreshToken(如 GUID),并存储它。 - **存储 RefreshToken**:使用 ABP 的缓存或数据库(推荐 `IDistributedCache` 或 `IRepository`)。引用[^2]建议将 RefreshToken 存储在缓存中(过期时间略长于 Access Token)。 ```csharp using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Caching; // 引入缓存命名空间 using Microsoft.Extensions.Caching.Memory; using System.IdentityModel.Tokens.Jwt; namespace YourProject.Controllers { [Route("api/auth")] public class AuthController : AbpController { private readonly IMemoryCache _memoryCache; // 使用内存缓存(生产环境可用 IDistributedCache) private readonly JwtSecurityTokenHandler _tokenHandler; public AuthController(IMemoryCache memoryCache) { _memoryCache = memoryCache; _tokenHandler = new JwtSecurityTokenHandler(); } [HttpPost("login")] public async Task<IActionResult> Login([FromBody] LoginModel model) { // 1. 验证用户凭据(使用 ABP Identity 模块) // 假设验证成功,获取用户 ID var userId = Guid.NewGuid(); // 示例:实际应从数据库获取 // 2. 生成 Access Token(使用 ABP 内置方法) var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim("sub", userId.ToString()) }), Expires = DateTime.UtcNow.AddMinutes(30), // Access Token 过期时间 SigningCredentials = new SigningCredentials(/* 使用步骤 1 的密钥 */) }; var accessToken = _tokenHandler.WriteToken(_tokenHandler.CreateToken(tokenDescriptor)); // 3. 生成 RefreshToken(GUID 格式) var refreshToken = Guid.NewGuid().ToString(); // 4. 存储 RefreshToken(缓存中,键为用户 ID,过期时间 7 天) _memoryCache.Set(refreshToken, userId, TimeSpan.FromDays(7)); // 5. 返回双 Token return Ok(new { AccessToken = accessToken, RefreshToken = refreshToken }); } } } ``` - **关键点**: - RefreshToken 应与用户关联(如用户 ID),便于后续验证。 - 生产环境推荐使用 `IDistributedCache`(如 Redis)代替 `IMemoryCache`,以支持分布式部署[^2][^3]。 - ABP 的 `IRepository` 可用于数据库存储(例如创建 `RefreshToken` 实体),但缓存更高效,适合高并发[^3]。 --- #### 步骤 3: **实现 Token 刷新端点** 创建一个 API 端点,用于使用 RefreshToken 获取新 Access Token: - **验证 RefreshToken**:检查缓存中是否存在未过期的 RefreshToken。 - **生成新 Access Token**:基于用户身份创建新令牌。 - **可选:更新 RefreshToken**:防止令牌重用(引用[^3]建议刷新时更新 RefreshToken)。 ```csharp [HttpPost("refresh")] public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenModel model) { // 1. 验证 RefreshToken 是否存在 if (!_memoryCache.TryGetValue(model.RefreshToken, out Guid userId)) { return Unauthorized("无效或过期的 RefreshToken"); } // 2. 生成新 Access Token var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim("sub", userId.ToString()) }), Expires = DateTime.UtcNow.AddMinutes(30), // 新 Access Token 过期时间 SigningCredentials = new SigningCredentials(/* 密钥 */) }; var newAccessToken = _tokenHandler.WriteToken(_tokenHandler.CreateToken(tokenDescriptor)); // 3. 可选:更新 RefreshToken(延长过期时间或生成新 GUID)[^3] var newRefreshToken = Guid.NewGuid().ToString(); _memoryCache.Remove(model.RefreshToken); // 移除旧 RefreshToken _memoryCache.Set(newRefreshToken, userId, TimeSpan.FromDays(7)); // 存储新 RefreshToken // 4. 返回新双 Token return Ok(new { AccessToken = newAccessToken, RefreshToken = newRefreshToken }); } public class RefreshTokenModel { public string RefreshToken { get; set; } } ``` - **关键点**: - 刷新时移除旧 RefreshToken 可防止令牌重用攻击[^3]。 - 此端点应标记为 `[AllowAnonymous]`,因为过期 Token 无法通过常规认证[^5]。 --- #### 步骤 4: **添加中间件实现无感刷新(可选)** 为了自动处理 Token 刷新(例如在 Access Token 快过期时),可创建全局中间件: - **检查 Access Token 过期时间**:如果接近过期(如剩余 30 分钟),使用 RefreshToken 生成新 Token(参考引用[^4])。 - **响应头返回新 Token**:客户端需处理新 Token 的更新。 ```csharp public class JwtRefreshMiddleware { private readonly RequestDelegate _next; private readonly IMemoryCache _memoryCache; public JwtRefreshMiddleware(RequestDelegate next, IMemoryCache memoryCache) { _next = next; _memoryCache = memoryCache; } public async Task InvokeAsync(HttpContext context) { // 1. 检查 Authorization Header var authorization = context.Request.Headers["Authorization"].ToString(); if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer")) { var tokenString = authorization.Substring("Bearer".Length).Trim(); var token = new JwtSecurityTokenHandler().ReadJwtToken(tokenString); // 2. 如果 Token 将在 30 分钟内过期 if (token.ValidTo > DateTime.UtcNow && token.ValidTo.AddMinutes(-30) <= DateTime.UtcNow) { // 3. 假设从缓存获取 RefreshToken(实际需根据用户 ID 查询) var userId = token.Claims.First(c => c.Type == "sub").Value; var refreshToken = _memoryCache.Get<string>(userId); // 简化示例 if (!string.IsNullOrEmpty(refreshToken)) { // 刷新逻辑(类似步骤 3) var newAccessToken = /* 生成新 Token */; context.Response.Headers.Add("X-Refresh-Token", newAccessToken); } } } await _next(context); // 继续处理请求 } } // 在 Startup.cs 或 HTTP 模块中注册中间件 public override void OnApplicationInitialization(ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); app.UseMiddleware<JwtRefreshMiddleware>(); // ...其他中间件 } ``` - **关键点**: - 此中间件在访问 API 前检查 Token,实现无感刷新[^4]。 - 生产环境需添加错误处理和日志记录(ABP 内置 `ILogger`)。 --- ### 安全注意事项和最佳实践 - **Token 存储**:使用加密存储(如 Redis 或数据库)。ABP 的 `IDistributedCache` 支持分布式锁,避免并发问题[^3]。 - **Token 撤销**:在用户登出或密码更改时,移除相关 RefreshToken。 - **HTTPS**:确保所有 API 使用 HTTPS,防止令牌泄露。 - **ABP 模块化**:将 RefreshToken 逻辑封装为独立模块(如 `YourProjectJwtExtensionsModule`),便于重用和测试。 - **性能**:缓存 RefreshToken 以减少数据库查询(引用[^2][^3] 推荐缓存机制)。 参考 ABP 官方文档进一步优化:[ABP Framework Authentication](https://docs.abp.io/en/abp/latest/Authentication)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hefeng_aspnet

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

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

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

打赏作者

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

抵扣说明:

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

余额充值