You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET 3.1 Web API中如何将JWT令牌保存至数据库?

解决JWT令牌保存到数据库的问题

一、先修复现有代码的核心问题

1. 完善AuthenticationToken模型

当前模型缺少主键(EF Core要求实体必须有主键),且未关联用户,无法区分令牌归属。修改后:

public class AuthenticationToken
{
    // EF Core自动识别Id为主键
    public int Id { get; set; }
    public string Token { get; set; }
    // 关联用户的外键
    public int UserId { get; set; }
    // 导航属性,关联到用户实体
    public Register User { get; set; }
    // 新增:记录令牌过期时间,方便后续清理过期数据
    public DateTime ExpiresAt { get; set; }
}

2. 调整JwtAuthContext上下文

Login是登录请求的DTO(仅用于接收请求参数),无需存入数据库,移除冗余的DbSet<Login>:

public class JwtAuthContext : DbContext
{
    public JwtAuthContext(DbContextOptions<JwtAuthContext> options)
        : base(options)
    {
    }

    public DbSet<Register> Registers { get; set; }
    // 规范命名,首字母大写
    public DbSet<AuthenticationToken> AuthenticationTokens { get; set; }

    // 显式配置外键关系,避免EF Core自动映射出错
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AuthenticationToken>()
            .HasOne(t => t.User)
            .WithMany()
            .HasForeignKey(t => t.UserId)
            .OnDelete(DeleteBehavior.Cascade); // 删除用户时自动清理关联令牌
    }
}

3. 修改LoginController的Login方法,实现令牌保存

生成令牌后,创建AuthenticationToken实例并添加到上下文,完成数据库存储:

[HttpPost]
public IActionResult Login([FromBody] Login login)
{
    var user = Authenticate(login);

    if (user != null)
    {
        var tokenString = Generate(user);
        // 解析令牌获取过期时间
        var tokenHandler = new JwtSecurityTokenHandler();
        var jwtToken = tokenHandler.ReadJwtToken(tokenString);
        
        // 构造令牌实体
        var authToken = new AuthenticationToken
        {
            Token = tokenString,
            UserId = user.Id,
            ExpiresAt = jwtToken.ValidTo
        };

        _context.AuthenticationTokens.Add(authToken);
        _context.SaveChanges(); // 关键步骤:将令牌写入数据库

        return Ok(new { Token = tokenString });
    }

    return NotFound("用户不存在");
}

4. 修正Generate方法的Claim映射错误

当前Claim类型与内容不匹配,修正后更符合规范:

private string Generate(Register user)
{
    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

    var claims = new[]
    {
        new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // 存储用户ID而非Email
        new Claim(ClaimTypes.Email, user.Email), // Email对应ClaimTypes.Email
        new Claim(ClaimTypes.Name, user.FullName), // 用户名对应ClaimTypes.Name
        new Claim(ClaimTypes.Role, user.Role ?? "User") // 处理Role为空的默认值
    };

    var token = new JwtSecurityToken(
        _config["Jwt:Issuer"],
        _config["Jwt:Audience"],
        claims,
        expires: DateTime.Now.AddMinutes(15),
        signingCredentials: credentials);

    return new JwtSecurityTokenHandler().WriteToken(token);
}

二、现有代码的其他风险提醒

  • 密码明文存储:当前注册直接存储明文密码,极易泄露,必须使用BCrypt、ASP.NET Core Identity的PasswordHasher等工具哈希后再存储。
  • 令牌过期清理:数据库中会累积大量过期令牌,建议定时执行清理任务(比如用Hangfire或后台服务)删除ExpiresAt小于当前时间的记录。
  • Login类冗余:Login仅作为请求参数载体,无需作为数据库实体,已从上下文移除,后续不要向Logins集合添加数据。

内容的提问来源于stack exchange,提问作者gourav kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 08:24:09