build-flow.api/src/modules/rag/Application/Auth/Handlers/RefreshTokenCommandHandler.cs
xiangning 17b7e9996e chore(dependencies): 移除未使用的 using 语句
- 从多个文件中移除未使用的 System.Linq.Dynamic.Core 引用
- 从多个文件中移除未使用的 Microsoft.EntityFrameworkCore 引用
- 从 IM 模块中移除未使用的 MongoDB.Driver.Linq 引用
- 清理 Handler 文件中的冗余命名空间引用
- 优化代码结构减少不必要的依赖引入
- 提高代码整洁性和编译效率
2026-07-02 14:53:56 +08:00

70 lines
3.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using BuildFlow.Shared.Domain.Exceptions;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using MediatR;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using BuildFlow.Application.Auth.DTOs;
using BuildFlow.Infrastructure.Persistence;
using BuildFlow.Application.Auth.Commands;
namespace BuildFlow.Application.Auth.Handlers;
/// <summary>
/// 刷新令牌Command处理器。执行业务校验后持久化。
/// </summary>
public class RefreshTokenCommandHandler(RagDbContext db, IConfiguration config) : IRequestHandler<RefreshTokenCommand, TokenResponse>
{
public async Task<TokenResponse> Handle(RefreshTokenCommand request, CancellationToken ct)
{
var stored = await db.RefreshTokens
.Include(rt => rt.User).ThenInclude(u => u.UserRoles).ThenInclude(ur => ur.Role).ThenInclude(r => r.RolePermissions).ThenInclude(rp => rp.Permission)
.FirstOrDefaultAsync(rt => rt.Token == request.RefreshToken, ct)
?? throw new UnauthorizedException("无效的刷新令牌");
// 已吊销或已过期均视为不可用,防止 refresh token 被复用
if (stored.IsRevoked || stored.ExpiresAt < DateTime.UtcNow)
throw new UnauthorizedException("刷新令牌已过期或已吊销");
// 旧 token 立即作废,强制下一次刷新必须用新 token(Rotation 防盗用)
stored.IsRevoked = true;
var user = stored.User;
var roles = user.UserRoles.Select(ur => ur.Role.Name).ToList();
var permissions = user.UserRoles.SelectMany(ur => ur.Role.RolePermissions).Select(rp => rp.Permission.Code).Distinct().ToList();
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Jwt:SigningKey"]!));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new List<Claim>
{
new(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
new(JwtRegisteredClaimNames.UniqueName, user.Username),
new(JwtRegisteredClaimNames.Email, user.Email),
};
claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r)));
claims.AddRange(permissions.Select(p => new Claim("permissions", p)));
var accessToken = new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(
issuer: config["Jwt:Issuer"],
audience: config["Jwt:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddMinutes(15),
signingCredentials: creds
));
var newRefresh = Convert.ToBase64String(Guid.NewGuid().ToByteArray()) + Guid.NewGuid().ToString("N");
await db.RefreshTokens.AddAsync(new Domain.Entities.RefreshToken
{
Id = Guid.NewGuid(),
UserId = user.Id,
Token = newRefresh,
ExpiresAt = DateTime.UtcNow.AddDays(7),
}, ct);
await db.SaveChangesAsync(ct);
return new TokenResponse(accessToken, newRefresh, DateTime.UtcNow.AddMinutes(15));
}
}