介紹
在 Web 開發領域,安全是重中之重。JSON Web Tokens (JWT) 已成為在各方之間安全傳輸信息的熱門選擇。然而,在 JWT 過期后,如何維護用戶會話并避免頻繁登錄至關重要。這正是 JWT 刷新令牌應運而生的地方。
在本文中,我們將指導您在 .NET 8.0 環境中實現 JWT 刷新令牌。我們將通過完整的示例介紹每個步驟,以確保清晰地理解整個過程。
JWT 和刷新令牌
JSON Web Tokens (JWT) 由三部分組成:標頭、有效負載和簽名。它們經過數字簽名以驗證其真實性,并包含聲明,這些聲明是關于實體(用戶)和其他數據的聲明。JWT 具有到期時間 (exp),到期后將被視為無效。刷新令牌用于在原始令牌到期后獲取新的 JWT,而無需用戶重新輸入其憑據。
設置.NET項目
讓我們使用 JWT 和刷新令牌創建一個簡單的身份驗證系統。
首先,確保您已安裝必要的包(System.IdentityModel.Tokens.Jwt 和 Microsoft.AspNetCore.Authentication.JwtBearer)。
模型
創建兩個模型來表示登錄請求和包含令牌的響應。
// LoginModel.cs
public class LoginModel
{
public string Username { get; set; }
public string Password { get; set; }
}
// TokenResponse.cs
public class TokenResponse
{
public string AccessToken { get; set; }
public string RefreshToken { get; set; }
}
授權控制器
創建一個負責處理身份驗證和令牌生成的 AuthController。
[ApiController]
[Route("[controller]")]
public class AuthController : ControllerBase
{
private readonly IConfiguration _config;
private readonly IUserService _userService;
? ? public AuthController(IConfiguration config, IUserService userService)
{
_config = config;
_userService = userService;
}
? ? [HttpPost("login")]
public IActionResult Login(LoginModel loginModel)
{
// Authenticate user
var user = _userService.Authenticate(loginModel.Username, loginModel.Password);
? ? ? ? if (user == null)
return Unauthorized();
? ? ? ? // Generate tokens
var accessToken = TokenUtils.GenerateAccessToken(user, _config["Jwt:Secret"]);
var refreshToken = TokenUtils.GenerateRefreshToken();
? ? ? ? // Save refresh token (for demo purposes, this might be stored securely in a database)
// _userService.SaveRefreshToken(user.Id, refreshToken);
? ? ? ? var response = new TokenResponse
{
AccessToken = accessToken,
RefreshToken = refreshToken
};
? ? ? ? return Ok(response);
}
? ? [HttpPost("refresh")]
public IActionResult Refresh(TokenResponse tokenResponse)
{
// For simplicity, assume the refresh token is valid and stored securely
// var storedRefreshToken = _userService.GetRefreshToken(userId);
? ? ? ? // Verify refresh token (validate against the stored token)
// if (storedRefreshToken != tokenResponse.RefreshToken)
// ? ?return Unauthorized();
? ? ? ? // For demonstration, let's just generate a new access token
var newAccessToken = TokenUtils.GenerateAccessTokenFromRefreshToken(tokenResponse.RefreshToken, _config["Jwt:Secret"]);
? ? ? ? var response = new TokenResponse
{
AccessToken = newAccessToken,
RefreshToken = tokenResponse.RefreshToken // Return the same refresh token
};
? ? ? ? return Ok(response);
}
}
Token Utility
創建一個實用程序類 TokenUtils 來處理令牌的生成和驗證。
public static class TokenUtils
{
public static string GenerateAccessToken(User user, string secret)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(secret);
? ? ? ? var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] { new Claim("id", user.Id.ToString()) }),
Expires = DateTime.UtcNow.AddMinutes(15), // Token expiration time
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
? ? ? ? var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
? ? public static string GenerateRefreshToken()
{
var randomNumber = new byte[32];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
? ? public static string GenerateAccessTokenFromRefreshToken(string refreshToken, string secret)
{
// Implement logic to generate a new access token from the refresh token
// Verify the refresh token and extract necessary information (e.g., user ID)
// Then generate a new access token
? ? ? ? // For demonstration purposes, return a new token with an extended expiry
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(secret);
? ? ? ? var tokenDescriptor = new SecurityTokenDescriptor
{
Expires = DateTime.UtcNow.AddMinutes(15), // Extend expiration time
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
? ? ? ? var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
}
用戶服務(示例)
為了演示目的,這里有一個提供用戶身份驗證的簡單 UserService。
public interface IUserService
{
User Authenticate(string username, string password);
// void SaveRefreshToken(int userId, string refreshToken);
// string GetRefreshToken(int userId);
}
public class UserService : IUserService
{
private readonly List<User> _users = new List<User>
{
new User { Id = 1, Username = "user1", Password = "password1" }
};
? ? public User Authenticate(string username, string password)
{
var user = _users.SingleOrDefault(x => x.Username == username && x.Password == password);
return user;
}
? ? // For demo purposes - methods to save and retrieve refresh tokens
}
用戶模型(示例)
創建一個簡單的用戶模型。
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
啟動配置
在您的 Startup.cs 中,配置 JWT 身份驗證。
public void ConfigureServices(IServiceCollection services)
{
// ...
? ? var secret = Configuration["Jwt:Secret"];
var key = Encoding.ASCII.GetBytes(secret);
? ? services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key)
};
});
? ? // ...
}
重要提示
這是一個用于演示的基本實現。在生產環境中,您應該增強安全性,安全地處理令牌存儲,并實現適當的驗證和錯誤處理。
為了更好的安全性,請考慮使用 IdentityServer4 庫或其他成熟的身份驗證解決方案。
結論
在 .NET 8.0 中實現 JWT 刷新令牌涉及配置身份驗證中間件、在身份驗證時生成令牌以及根據需要刷新過期令牌。此過程允許無縫令牌更新,而無需用戶重復登錄,從而增強了安全性。
記住要安全地處理令牌存儲并實施適當的驗證邏輯,以確保身份驗證系統的安全性和完整性。
如果您喜歡此文章,請收藏、點贊、評論,謝謝,祝您快樂每一天。