[英]How to implement JWT Refresh Tokens in asp.net core web api (no 3rd party)?
I'm in the process of implementing a web api using asp.net core that is using JWT. 我正在使用使用JWT的asp.net核心实现web api。 I am not using a third party solution such as IdentityServer4 as I am trying to learn.
我正在尝试学习,我没有使用像IdentityServer4这样的第三方解决方案。
I've gotten the JWT configuration to work, but am stumped on how to implement refresh tokens for when the JWT's expire. 我已经让JWT配置工作了,但我很难知道如何在JWT到期时实现刷新令牌。
Below is some sample code in my Configure method inside startup.cs. 下面是startup.cs中我的Configure方法中的一些示例代码。
app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
AuthenticationScheme = "Jwt",
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = Configuration["Tokens:Audience"],
ValidIssuer = Configuration["Tokens:Issuer"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"])),
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
}
});
Below is the Controller method used to generate the JWT. 下面是用于生成JWT的Controller方法。 I have set expiration to 30 seconds for testing purposes.
为测试目的,我将到期时间设置为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.");
}
Would be very grateful for some guidance. 非常感谢一些指导。
First of all, you need to generate a refresh token and persist it somewhere. 首先,您需要生成一个刷新令牌并将其保留在某处。 This is because you want to be able to invalidate it if needed.
这是因为您希望能够在需要时使其无效。 If you were to follow the same pattern as an access token - where all the data is contained within the token - a token that ends up in the wrong hands can the be used to generate new access tokens for the lifetime of the refresh token, which can be a really long time.
如果您遵循与访问令牌相同的模式 - 其中所有数据都包含在令牌中 - 最终落入坏人手中的令牌可用于在刷新令牌的生命周期内生成新的访问令牌,可能很长一段时间。
So what the heck do you need to persist? 那么你需要坚持到底是什么?
You need a unique identifier of some sort that's not easy guessable, a GUID will do just fine. 你需要一种不容易猜到的某种唯一标识符,一个GUID就可以了。 You also need the data to be able to issue a new access token, most likely a username.
您还需要数据能够发出新的访问令牌,很可能是用户名。 Having a username you can then skip the VerifyHashedPassword(...)-part but for the rest of it, just follow the same logic.
拥有用户名后,您可以跳过VerifyHashedPassword(...) - 部分但是对于其余部分,只需遵循相同的逻辑。
To obtain a refresh token, you normally use the scope "offline_access", this being something that you provide in your model (CredentialViewModel) when making a token request. 要获取刷新令牌,通常使用范围“offline_access”,这是您在发出令牌请求时在模型(CredentialViewModel)中提供的内容。 Unlike a normal access token request, you don't need to provide your username and password, but instead the refresh token.
与普通访问令牌请求不同,您不需要提供用户名和密码,而是提供刷新令牌。 When getting a request with a refresh token, you lookup the persisted identifier and issue a token for the user found.
使用刷新令牌获取请求时,您将查找持久标识符并为找到的用户发出令牌。
Following is pseudo code for the happy path: 以下是快乐路径的伪代码:
[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)
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.