2017-04-18 55 views
8

我正在使用使用JWT的asp.net核心實現web api。我沒有使用第三方解決方案,如IdentityServer4,因爲我正在嘗試學習。如何在asp.net核心web API(無第三方)中實現JWT刷新令牌?

我已經得到了JWT配置的工作,但我很難在如何爲JWT過期時實施刷新令牌。

下面是我在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 
    } 
}); 

以下是用於生成JWT的Controller方法。爲了測試目的,我已將過期設置爲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."); 
    } 

非常感謝一些指導。

+0

您發佈的代碼只是爲了驗證訪問令牌相關,不刷新令牌。你能分享你生成訪問令牌的代碼嗎? – naslund

+0

當然,我已經添加了用於生成JWT的代碼。 – DJDJ

回答

7

首先,您需要生成刷新令牌並將其保留在某處。這是因爲如果需要,您希望能夠使其失效。如果您要遵循與訪問令牌相同的模式(即所有數據都包含在令牌中),則可以使用以錯誤的方式結束的令牌,以便在刷新令牌的生命週期中生成新的訪問令牌,可能會很長時間。

那麼你需要堅持什麼? 您需要一些不容易猜測的某種唯一標識符,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) 
     } 
    } 
} 
+0

謝謝您的輸入! – DJDJ

相關問題