C# 如何在asp.net核心web api中实现JWT刷新令牌(无第三方)?

C# 如何在asp.net核心web api中实现JWT刷新令牌(无第三方)?,c#,asp.net,asp.net-web-api,C#,Asp.net,Asp.net Web Api,我正在使用asp.net核心实现一个web api,该核心使用JWT。我没有使用第三方解决方案,如我试图了解的IdentityServer4 我已经让JWT配置正常工作了,但是在JWT过期时如何实现刷新令牌却让我感到困惑 下面是startup.cs中我的Configure方法中的一些示例代码 app.UseJwtBearerAuthentication(新的JwtBearerOptions() { AuthenticationScheme=“Jwt”, 自动验证=真, 自动挑战=正确, Toke

我正在使用asp.net核心实现一个web api,该核心使用JWT。我没有使用第三方解决方案,如我试图了解的IdentityServer4

我已经让JWT配置正常工作了,但是在JWT过期时如何实现刷新令牌却让我感到困惑

下面是startup.cs中我的Configure方法中的一些示例代码

app.UseJwtBearerAuthentication(新的JwtBearerOptions()
{
AuthenticationScheme=“Jwt”,
自动验证=真,
自动挑战=正确,
TokenValidationParameters=新的TokenValidationParameters()
{
Validudience=配置[“令牌:受众”],
ValidisUser=配置[“令牌:颁发者”],
ValidateSuersigningKey=true,
IssuerSigningKey=new-SymmetricSecurityKey(Encoding.UTF8.GetBytes(配置[“令牌:密钥])),
ValidateLifetime=true,
时钟偏移=时间跨度0
}
});
下面是用于生成JWT的控制器方法。出于测试目的,我已将过期时间设置为30秒

    [Route("Token")]
    [HttpPost]
    public async Task<IActionResult> CreateToken([FromBody] CredentialViewModel model)
    {
        try
        {
            var user = await _userManager.FindByNameAsync(model.Username);

            if (user != null)
            {
                if (_hasher.VerifyHashedPassword(user, user.PasswordHash, model.Password) == PasswordVerificationResult.Success)
                {
                    var userClaims = await _userManager.GetClaimsAsync(user);

                    var claims = new[]
                    {
                        new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
                    }.Union(userClaims);

                    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwt.Key));
                    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

                    var token = new JwtSecurityToken(
                            issuer: _jwt.Issuer,
                            audience: _jwt.Audience,
                            claims: claims,
                            expires: DateTime.UtcNow.AddSeconds(30),
                            signingCredentials: creds
                        );

                    return Ok(new
                    {
                        access_token = new JwtSecurityTokenHandler().WriteToken(token),
                        expiration = token.ValidTo
                    });
                }
            }
        }
        catch (Exception)
        {

        }

        return BadRequest("Failed to generate token.");
    }
[路由(“令牌”)]
[HttpPost]
公共异步任务CreateToken([FromBody]CredentialViewModel模型)
{
尝试
{
var user=await\u userManager.FindByNameAsync(model.Username);
如果(用户!=null)
{
if(_hasher.VerifyHashedPassword(user,user.PasswordHash,model.Password)==PasswordVerificationResult.Success)
{
var userClaims=await_userManager.GetClaimsAsync(用户);
风险值索赔=新[]
{
新声明(JwtRegisteredClaimNames.Sub,user.UserName),
新声明(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString())
}.Union(用户索赔);
var key=new-SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwt.key));
var creds=新的签名凭证(key,SecurityAlgorithms.HmacSha256);
var token=新的JwtSecurityToken(
发行人:_jwt.issuer,
观众:_jwt.观众,
索赔:索赔,
过期:DateTime.UtcNow.AddSeconds(30),
签署证书:信誉
);
返回Ok(新的
{
access_token=new JwtSecurityTokenHandler().WriteToken(令牌),
expiration=token.ValidTo
});
}
}
}
捕获(例外)
{
}
返回BadRequest(“生成令牌失败”);
}

非常感谢您的指导。

首先,您需要生成一个刷新令牌并将其保存到某个地方。这是因为您希望能够在需要时使其无效。如果您要遵循与访问令牌相同的模式(其中所有数据都包含在令牌中),那么最终落入坏人手中的令牌可以用于在刷新令牌的生命周期内生成新的访问令牌,刷新令牌的生命周期可能非常长

那么你到底需要坚持什么呢

您需要一个不容易猜到的唯一标识符,GUID就可以了。您还需要这些数据才能发布新的访问令牌,很可能是用户名。拥有用户名后,您可以跳过VerifyHashedPassword(…)-部分,但对于其余部分,只需遵循相同的逻辑即可

要获取刷新令牌,通常使用范围“offline_access”,这是您在发出令牌请求时在模型(CredentialViewModel)中提供的内容。与普通的访问令牌请求不同,您不需要提供用户名和密码,而是需要提供刷新令牌。当获取具有刷新令牌的请求时,可以查找持久化标识符并为找到的用户发出令牌

以下是快乐路径的伪代码:

[Route("Token")]
[HttpPost]
public async Task<IActionResult> CreateToken([FromBody] CredentialViewModel model)
{
    if (model.GrantType is "refresh_token")
    {
        // Lookup which user is tied to model.RefreshToken
        // Generate access token from the username (no password check required)
        // Return the token (access + expiration)
    }
    else if (model.GrantType is "password")
    {
        if (model.Scopes contains "offline_access")
        {
            // Generate access token
            // Generate refresh token (random GUID + model.username)
            // Persist refresh token
            // Return the complete token (access + refresh + expiration)
        }
        else
        {
            // Generate access token
            // Return the token (access + expiration)
        }
    }
}
[路由(“令牌”)]
[HttpPost]
公共异步任务CreateToken([FromBody]CredentialViewModel模型)
{
if(model.GrantType为“刷新令牌”)
{
//查找绑定到model.RefreshToken的用户
//从用户名生成访问令牌(无需密码检查)
//返回令牌(访问+过期)
}
else if(model.GrantType为“密码”)
{
if(model.Scopes包含“脱机访问”)
{
//生成访问令牌
//生成刷新令牌(随机GUID+model.username)
//持久刷新令牌
//返回完整的令牌(访问+刷新+过期)
}
其他的
{
//生成访问令牌
//返回令牌(访问+过期)
}
}
}

您发布的代码仅与验证访问令牌相关,而与刷新令牌无关。你能在生成访问令牌的地方分享代码吗?当然,我已经添加了用于生成JWT的代码。谢谢你的输入!