Files
TakeoutSaaS.AdminApi/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Services/RedisRefreshTokenStore.cs

54 lines
2.3 KiB
C#

using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using System.Security.Cryptography;
using System.Text.Json;
using TakeoutSaaS.Application.Identity.Abstractions;
using TakeoutSaaS.Application.Identity.Models;
using TakeoutSaaS.Infrastructure.Identity.Options;
namespace TakeoutSaaS.Infrastructure.Identity.Services;
/// <summary>
/// Redis 刷新令牌存储。
/// </summary>
public sealed class RedisRefreshTokenStore(IDistributedCache cache, IOptions<RefreshTokenStoreOptions> options) : IRefreshTokenStore
{
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
private readonly RefreshTokenStoreOptions _options = options.Value;
public async Task<RefreshTokenDescriptor> IssueAsync(long userId, DateTime expiresAt, CancellationToken cancellationToken = default)
{
var token = Convert.ToBase64String(RandomNumberGenerator.GetBytes(48));
var descriptor = new RefreshTokenDescriptor(token, userId, expiresAt, false);
var key = BuildKey(token);
var entryOptions = new DistributedCacheEntryOptions { AbsoluteExpiration = expiresAt };
await cache.SetStringAsync(key, JsonSerializer.Serialize(descriptor, JsonOptions), entryOptions, cancellationToken);
return descriptor;
}
public async Task<RefreshTokenDescriptor?> GetAsync(string refreshToken, CancellationToken cancellationToken = default)
{
var json = await cache.GetStringAsync(BuildKey(refreshToken), cancellationToken);
return string.IsNullOrWhiteSpace(json)
? null
: JsonSerializer.Deserialize<RefreshTokenDescriptor>(json, JsonOptions);
}
public async Task RevokeAsync(string refreshToken, CancellationToken cancellationToken = default)
{
var descriptor = await GetAsync(refreshToken, cancellationToken);
if (descriptor == null)
{
return;
}
var updated = descriptor with { Revoked = true };
var entryOptions = new DistributedCacheEntryOptions { AbsoluteExpiration = updated.ExpiresAt };
await cache.SetStringAsync(BuildKey(refreshToken), JsonSerializer.Serialize(updated, JsonOptions), entryOptions, cancellationToken);
}
private string BuildKey(string token) => $"{_options.Prefix}{token}";
}