在.NET Core 2 Web Api中验证JWT后获取用户数据的最佳做法是什么?

问题描述 投票:3回答:1

我正在从MVC转向SPA + API方法,我正在制作一个新的应用程序,我正面临一些我似乎无法找到答案的问题。

我在.Net Core 2中有一个Web API,它使用Identity作为其用户存储。我通过发出JWT令牌来保护API,我的SPA在每次向我的控制器发出请求时作为承载令牌发送。发行代码是:

    private async Task<object> GenerateJwtTokenAsync(ApplicationUser user)
    {
        var roles = await _userManager.GetRolesAsync(user);

        var claims = new List<Claim>
        {
            new Claim(JwtRegisteredClaimNames.Sub, user.Email),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(ClaimTypes.NameIdentifier, user.Id)
        };
        claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtKey"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        var expires = DateTime.Now.AddDays(Convert.ToDouble(_configuration["JwtExpireDays"]));

        var token = new JwtSecurityToken(
            _configuration["JwtIssuer"],
            _configuration["JwtIssuer"],
            claims,
            expires: expires,
            signingCredentials: creds
        );

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

我的问题是:获取在控制器中提供请求所需的用户信息的最佳做法是什么?

当我获得JWT令牌时,我有用户信息,但是关于用户的扩展信息,例如他们所在的公司是在我的SQL数据库中。

// GET: api/Agreements
    [HttpGet]
    public async Task<IEnumerable<Agreement>> GetAgreementsAsync()
    {
        var user = await _userManager.GetUserAsync(HttpContext.User);

        return _context.Agreements.Where(x => x.CompanyId == user.CompanyId);
    }

我是否需要转向数据库才能获得有关每个请求的信息?是否应将此信息放入JWT令牌中,在这种情况下,您在哪个字段中放置“自定义”信息?

asp.net-core authorization asp.net-identity jwt asp.net-core-2.0
1个回答
9
投票

优选地,在授权时,您希望保持无状态,这意味着当客户端通过身份验证并获取JWT令牌时,服务器可以动态地使用JWT令牌授权请求。这意味着服务器不会在任何地方查找JWT令牌,也不会在数据库或内存中查找。因此,您没有查看数据库或其他地方的开销。

要回答你的问题,你也应该知道索赔背后的原因:“与给定实体相关的权利要求集可以被认为是一个关键。特定的权利要求定义了该密钥的形状;就像物理密钥用来打开一样锁门。通过这种方式,索赔用于获取资源。“来自MSDN

您不需要向DB提出另一个请求,但请记住,声明最好用于授权目的,因此主要在JWT中添加额外声明,以便您以后可以在不进入数据库的情况下授权API资源。您还可以将自定义声明添加到令牌,然后将其加密到JWT令牌并发送到客户端。

身份验证后,您可以将companyId和/或userId存储在声明中,您可以命名任何您想要的字符串,因为这是声明构造函数的实现方式。当请求到达时,您可以从索赔中获得companyId。例如,将其命名为“companyId”。

var claims = new List<Claim>
    {
        new Claim(JwtRegisteredClaimNames.Sub, user.Email),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim(ClaimTypes.NameIdentifier, user.Id),
        new Claim("companyId", user.companyId.ToString()) // Like this
    };

然后为公司ID索赔写getter

HttpContext.User.FindFirst("companyId").Value

另请注意,对于复杂的用户数据,您不应使用此类声明,因为此数据在网络中传递,您不需要巨大的JWT令牌。此外,这不是一个好习惯,因为您可以使用HttpContext.Session,您可以在其中存储数据并在请求到来时获取数据。这不是详细写入会话存储的地方。

© www.soinside.com 2019 - 2024. All rights reserved.