Cyrus Zhou 1 год назад
Родитель
Сommit
2e37f34d22

+ 6 - 0
Admin.NET/Admin.NET.Core/Enum/SysUserEventTypeEnum.cs

@@ -78,4 +78,10 @@ public enum SysUserEventTypeEnum
     /// </summary>
     [Description("用户退出")]
     LoginOut = 1001,
+
+    /// <summary>
+    /// RefreshToken
+    /// </summary>
+    [Description("刷新Token")]
+    RefreshToken = 1002,
 }

+ 46 - 14
Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs

@@ -7,6 +7,7 @@
 using Furion.SpecificationDocument;
 using Lazy.Captcha.Core;
 using NewLife.Reflection;
+using System.Threading.Tasks;
 
 namespace Admin.NET.Core.Service;
 
@@ -224,9 +225,10 @@ public class SysAuthService : IDynamicApiController, ITransient
     /// 生成Token令牌 🔖
     /// </summary>
     /// <param name="user"></param>\
+    /// <param name="sysUserEventTypeEnum"></param>\
     /// <returns></returns>
     [NonAction]
-    internal virtual async Task<LoginOutput> CreateToken(SysUser user)
+    internal virtual async Task<LoginOutput> CreateToken(SysUser user, SysUserEventTypeEnum sysUserEventTypeEnum = SysUserEventTypeEnum.Login)
     {
         // 单用户登录
         await _sysOnlineUserService.SingleLogin(user.Id);
@@ -279,7 +281,7 @@ public class SysAuthService : IDynamicApiController, ITransient
         };
 
         // 发布系统用户操作事件
-        await _eventPublisher.PublishAsync(SysUserEventTypeEnum.Login, payload);
+        await _eventPublisher.PublishAsync(sysUserEventTypeEnum, payload);
         return payload.Output;
     }
 
@@ -329,27 +331,57 @@ public class SysAuthService : IDynamicApiController, ITransient
     /// <summary>
     /// 获取刷新Token 🔖
     /// </summary>
-    /// <param name="accessToken"></param>
-    /// <returns></returns>
+    /// <param name="accessToken">旧的AccessToken</param>
+    /// <returns>新的AccessToken和RefreshToken</returns>
     [DisplayName("获取刷新Token")]
-    public virtual string GetRefreshToken([FromQuery] string accessToken)
+    public virtual async Task<LoginOutput> GetRefreshToken([FromQuery] string accessToken)
     {
-        var refreshTokenExpire = _sysConfigService.GetRefreshTokenExpire().GetAwaiter().GetResult();
-        return JWTEncryption.GenerateRefreshToken(accessToken, refreshTokenExpire);
+        var httpContext = _httpContextAccessor.HttpContext;
+        if (httpContext == null) throw Oops.Oh(ErrorCodeEnum.D1016);
+
+        if (string.IsNullOrWhiteSpace(accessToken)) throw Oops.Oh(ErrorCodeEnum.D1011);
+
+        if (string.IsNullOrWhiteSpace(_userManager.Account)) throw Oops.Oh(ErrorCodeEnum.D1011);
+
+        // 黑名单校验
+        if (_sysCacheService.ExistKey($"blacklist:token:{accessToken}")) throw Oops.Oh(ErrorCodeEnum.D1011);
+
+        // 解析Token
+        var (isValid, tokenData, validationResult) = JWTEncryption.Validate(accessToken);
+        if (isValid) throw Oops.Oh(ErrorCodeEnum.D1016);
+
+        // 获取用户Id
+        var user = await _sysUserRep.AsQueryable().ClearFilter().FirstAsync(u => u.Id == _userManager.UserId) ?? throw Oops.Oh(ErrorCodeEnum.D1011).StatusCode(401);
+        return await CreateToken(user, SysUserEventTypeEnum.RefreshToken);
     }
 
     /// <summary>
     /// 退出系统 🔖
     /// </summary>
     [DisplayName("退出系统")]
-    public void Logout()
+    public async Task Logout()
     {
-        // 发布系统用户操作事件
-        _ = _eventPublisher.PublishAsync(SysUserEventTypeEnum.LoginOut, new { Entity = _sysUserRep.GetById(_userManager.UserId) });
-        if (string.IsNullOrWhiteSpace(_userManager.Account)) throw Oops.Oh(ErrorCodeEnum.D1011);
-        var token = _httpContextAccessor.HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
-        _sysCacheService.Set($"blacklist:token:{token}", "1");
-        _httpContextAccessor.HttpContext.SignoutToSwagger();
+        var httpContext = _httpContextAccessor.HttpContext;
+        if (httpContext == null) throw Oops.Oh(ErrorCodeEnum.D1016);
+
+        var token = httpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
+
+        if (string.IsNullOrWhiteSpace(token))
+            throw Oops.Oh(ErrorCodeEnum.D1011);
+
+        if (string.IsNullOrWhiteSpace(_userManager.Account))
+            throw Oops.Oh(ErrorCodeEnum.D1011);
+
+        // 写入黑名单(设置过期时间,避免Redis膨胀)
+        var tokenExpire = await _sysConfigService.GetTokenExpire();
+        _sysCacheService.Set($"blacklist:token:{token}", "1", TimeSpan.FromMinutes(tokenExpire));
+
+        // 发布登出事件(用户退出)
+        var user = await _sysUserRep.GetByIdAsync(_userManager.UserId);
+        await _eventPublisher.PublishAsync(SysUserEventTypeEnum.LoginOut, new { Entity = user });
+
+        // 清除 Swagger 登录信息
+        httpContext.SignoutToSwagger();
     }
 
     /// <summary>

+ 4 - 2
Admin.NET/Admin.NET.Web.Core/Handlers/JwtHandler.cs

@@ -30,9 +30,11 @@ public class JwtHandler : AppAuthorizeHandler
     /// <returns></returns>
     public override async Task HandleAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext)
     {
-        // 若当前账号存在黑名单中则授权失败
+        var userId = context.User.FindFirst(ClaimConst.UserId)?.Value;
         var token = httpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
-        if (_sysCacheService.ExistKey($"{CacheConst.KeyBlacklist}{context.User.FindFirst(ClaimConst.UserId)?.Value}") ||
+
+        // 🛡️ 黑名单校验(包括用户和token)
+        if (_sysCacheService.ExistKey($"{CacheConst.KeyBlacklist}{userId}") ||
             _sysCacheService.ExistKey($"blacklist:token:{token}"))
         {
             context.Fail();