using IM.Commons; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json; using StackExchange.Redis; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; using System.Text; using System.Text.Json; namespace IM.Jwt { public class TokenService : ITokenService { private readonly IDatabase _redis; private readonly IOptions _options; public TokenService(IConnectionMultiplexer multiplexer, IOptions options) { _redis = multiplexer.GetDatabase(); _options = options; } private static string GenerateTokenStr() { var bytes = RandomNumberGenerator.GetBytes(32); return Convert.ToBase64String(bytes); } public async Task CreateRefreshTokenAsync(Guid userId, CancellationToken cancellationToken = default, string? stamp = null, int? days = null) { string token = GenerateTokenStr(); var payload = new { UserId = userId, CreateAt = DateTime.UtcNow, Stamp = stamp }; string json = JsonConvert.SerializeObject(payload); //token写入redis await _redis.StringSetAsync(RedisHelper.GetRefreshTokenKey(token), json, TimeSpan.FromDays(days is > 0 ? days.Value : _options.Value.RefreshTokenDays)); return token; } public string GetToken(IEnumerable claims, JwtOptions options) { TimeSpan ExpiryDuration = TimeSpan.FromMinutes(options.AccessTokenMinutes); var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.Key)); var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature); var tokenDescriptor = new JwtSecurityToken(options.Issuer, options.Audience, claims, expires: DateTime.Now.Add(ExpiryDuration), signingCredentials: credentials); return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor); } public async Task RevokeRefreshTokenAsync(string refreshToken) { await _redis.KeyDeleteAsync(RedisHelper.GetRefreshTokenKey(refreshToken)); } public async Task<(bool ok, Guid userId, string? stamp)> ValidateRefreshTokenAsync(string token, CancellationToken cancellation = default) { var json = await _redis.StringGetAsync(RedisHelper.GetRefreshTokenKey(token)); if (json.IsNullOrEmpty) return (false, Guid.Empty, null); try { using var doc = JsonDocument.Parse(json.ToString()); var userId = doc.RootElement.GetProperty("UserId").GetGuid(); return (true, userId, doc.RootElement.TryGetProperty("Stamp", out var stamp) ? stamp.GetString() : null); } catch { return (false, Guid.Empty, null); } } } }