67 lines
2.0 KiB
C#
67 lines
2.0 KiB
C#
using Microsoft.Extensions.Caching.Distributed;
|
||
using Microsoft.Extensions.Options;
|
||
using System.Security.Cryptography;
|
||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||
using TakeoutSaaS.Infrastructure.Identity.Options;
|
||
|
||
namespace TakeoutSaaS.Infrastructure.Identity.Services;
|
||
|
||
/// <summary>
|
||
/// Redis 管理后台重置密码链接令牌存储。
|
||
/// </summary>
|
||
public sealed class RedisAdminPasswordResetTokenStore(
|
||
IDistributedCache cache,
|
||
IOptions<AdminPasswordResetOptions> options)
|
||
: IAdminPasswordResetTokenStore
|
||
{
|
||
private readonly AdminPasswordResetOptions _options = options.Value;
|
||
|
||
/// <inheritdoc />
|
||
public async Task<string> IssueAsync(long userId, DateTime expiresAt, CancellationToken cancellationToken = default)
|
||
{
|
||
// 1. 生成 URL 安全的随机令牌
|
||
var token = GenerateUrlSafeToken(48);
|
||
|
||
// 2. 写入缓存(Value:userId)
|
||
await cache.SetStringAsync(BuildKey(token), userId.ToString(), new DistributedCacheEntryOptions
|
||
{
|
||
AbsoluteExpiration = expiresAt
|
||
}, cancellationToken);
|
||
|
||
// 3. 返回令牌
|
||
return token;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<long?> ConsumeAsync(string token, CancellationToken cancellationToken = default)
|
||
{
|
||
// 1. 读取缓存
|
||
var key = BuildKey(token);
|
||
var value = await cache.GetStringAsync(key, cancellationToken);
|
||
if (string.IsNullOrWhiteSpace(value))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
// 2. 删除缓存(一次性令牌)
|
||
await cache.RemoveAsync(key, cancellationToken);
|
||
|
||
// 3. 解析用户 ID
|
||
return long.TryParse(value, out var userId) ? userId : null;
|
||
}
|
||
|
||
private string BuildKey(string token) => $"{_options.Prefix}{token}";
|
||
|
||
private static string GenerateUrlSafeToken(int bytesLength)
|
||
{
|
||
var bytes = RandomNumberGenerator.GetBytes(bytesLength);
|
||
var token = Convert.ToBase64String(bytes);
|
||
|
||
return token
|
||
.Replace('+', '-')
|
||
.Replace('/', '_')
|
||
.TrimEnd('=');
|
||
}
|
||
}
|
||
|