Files
IM_NEW/IM.Jwt/TokenService.cs

71 lines
3.0 KiB
C#

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<JwtOptions> _options;
public TokenService(IConnectionMultiplexer multiplexer, IOptions<JwtOptions> options)
{
_redis = multiplexer.GetDatabase();
_options = options;
}
private static string GenerateTokenStr()
{
var bytes = RandomNumberGenerator.GetBytes(32);
return Convert.ToBase64String(bytes);
}
public async Task<string> 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<Claim> 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);
}
}
}
}