diff --git a/Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs b/Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs
index d35f2b5a..0394d3df 100644
--- a/Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs
+++ b/Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs
@@ -440,4 +440,15 @@ public class SysAuthService : IDynamicApiController, ITransient
return 401;
}
}
+
+ ///
+ /// 刷新token
+ ///
+ ///
+ [NonAction]
+ public async Task RefreshToken(long userId)
+ {
+ var user = await _sysUserRep.AsQueryable().IgnoreTenant().Includes(u => u.SysOrg).FirstAsync(u => u.Id == userId);
+ await CreateToken(user);
+ }
}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Service/Cache/SysCacheService.cs b/Admin.NET/Admin.NET.Core/Service/Cache/SysCacheService.cs
index efc3a340..8dcb366f 100644
--- a/Admin.NET/Admin.NET.Core/Service/Cache/SysCacheService.cs
+++ b/Admin.NET/Admin.NET.Core/Service/Cache/SysCacheService.cs
@@ -457,6 +457,17 @@ public class SysCacheService : IDynamicApiController, ISingleton
return _cacheProvider.Cache.ContainsKey($"{_cacheOptions.Prefix}{key}");
}
+ ///
+ /// 检查缓存是否不存在
+ ///
+ /// 键
+ ///
+ [NonAction]
+ public bool NotExistKey(string key)
+ {
+ return !_cacheProvider.Cache.ContainsKey($"{_cacheOptions.Prefix}{key}");
+ }
+
///
/// 根据键名前缀删除缓存 🔖
///
diff --git a/Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs b/Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs
index caf7c9fa..8efd83a5 100644
--- a/Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs
+++ b/Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs
@@ -168,6 +168,9 @@ public class SysUserService : IDynamicApiController, ITransient
// 更新域账号
await _sysUserLdapService.AddUserLdap(user.TenantId!.Value, user.Id, user.Account, input.DomainAccount);
+ // 清除用户session
+ _userManager.RemoveSession(input.Id);
+
// 发布更新用户事件
await _eventPublisher.PublishAsync(UserEventTypeEnum.Update, input);
}
@@ -246,8 +249,11 @@ public class SysUserService : IDynamicApiController, ITransient
[DisplayName("更新用户基本信息")]
public virtual async Task UpdateBaseInfo(SysUser user)
{
- return await _sysUserRep.AsUpdateable(user)
+ var count = await _sysUserRep.AsUpdateable(user)
.IgnoreColumns(u => new { u.CreateTime, u.Account, u.Password, u.AccountType, u.OrgId, u.PosId }).ExecuteCommandAsync();
+ // 清除用户session
+ _userManager.RemoveSession(user.Id);
+ return count;
}
///
@@ -492,5 +498,8 @@ public class SysUserService : IDynamicApiController, ITransient
// 强制下线账号
await _sysOnlineUserService.ForceOfflineByUserId(user.Id);
+
+ // 清除用户session
+ _userManager.RemoveSession(user.Id);
}
}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Service/User/UserManager.cs b/Admin.NET/Admin.NET.Core/Service/User/UserManager.cs
index 3c68fd0e..a408753f 100644
--- a/Admin.NET/Admin.NET.Core/Service/User/UserManager.cs
+++ b/Admin.NET/Admin.NET.Core/Service/User/UserManager.cs
@@ -27,7 +27,14 @@ public class UserManager(
///
[System.Text.Json.Serialization.JsonIgnore]
[Newtonsoft.Json.JsonIgnore]
- protected virtual UserSessionDao Session => _session ??= sysCacheService.Get(CacheConst.KeyUserSession + UserId);
+ protected virtual UserSessionDao Session
+ {
+ get
+ {
+ if (_session == null || _session.UserId != UserId) _session = sysCacheService.Get(CacheConst.KeyUserSession + UserId);
+ return _session;
+ }
+ }
///
/// 用户Id
diff --git a/Admin.NET/Admin.NET.Web.Core/Handlers/JwtHandler.cs b/Admin.NET/Admin.NET.Web.Core/Handlers/JwtHandler.cs
index 8ea1021c..bc5fef16 100644
--- a/Admin.NET/Admin.NET.Web.Core/Handlers/JwtHandler.cs
+++ b/Admin.NET/Admin.NET.Web.Core/Handlers/JwtHandler.cs
@@ -62,7 +62,7 @@ namespace Admin.NET.Web.Core
// 验证Token黑名单
var userId = httpContext.User.FindFirst(ClaimConst.UserId)?.Value;
var version = httpContext.User.FindFirst(ClaimConst.TokenVersion)?.Value;
- if (sysCacheService.ExistKey($"{CacheConst.KeyTokenBlacklist}{userId}:{version}") || !sysCacheService.ExistKey($"{CacheConst.KeyUserSession}{userId}"))
+ if (sysCacheService.ExistKey($"{CacheConst.KeyTokenBlacklist}{userId}:{version}"))
{
context.Fail(new AuthorizationFailureReason(this, "令牌已失效,请重新登录。"));
context.StatusCode(StatusCodes.Status401Unauthorized);
@@ -70,6 +70,14 @@ namespace Admin.NET.Web.Core
return;
}
+ // 刷新 Session
+ if (sysCacheService.NotExistKey($"{CacheConst.KeyUserSession}{userId}"))
+ {
+ var sysAuthService = serviceScope.ServiceProvider.GetRequiredService();
+ await sysAuthService.RefreshToken(long.Parse(userId!));
+ return;
+ }
+
if (!string.IsNullOrWhiteSpace(userId))
{
// 查库并缓存用户Token版本
diff --git a/Web/src/types/pinia.d.ts b/Web/src/types/pinia.d.ts
index ad101822..1a372c0f 100644
--- a/Web/src/types/pinia.d.ts
+++ b/Web/src/types/pinia.d.ts
@@ -46,6 +46,7 @@ declare interface RoutesListState {
// 布局配置
declare interface ThemeConfigState {
themeConfig: {
+ serverTime?: Date; // 服务器时间
isDrawer: boolean; // 是否开启抽屉配置
primary: string; // 主题颜色
topBar: string; // 顶部栏背景
diff --git a/Web/src/utils/axios-utils.ts b/Web/src/utils/axios-utils.ts
index 2331a9c1..343ae73f 100644
--- a/Web/src/utils/axios-utils.ts
+++ b/Web/src/utils/axios-utils.ts
@@ -11,6 +11,11 @@ import { BaseAPI, BASE_PATH } from '../api-services/system/base';
import { ElMessage } from 'element-plus';
import { Local, Session } from '../utils/storage';
+import {useThemeConfig} from "/@/stores/themeConfig";
+import {storeToRefs} from "pinia";
+
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
// 接口服务器配置
export const serveConfig = new Configuration({
@@ -66,7 +71,7 @@ axiosInstance.interceptors.request.use(
const exp = getJWTDate(jwt.exp as number);
// token 已经过期
- if (new Date() >= exp) {
+ if (themeConfig.value.serverTime && themeConfig.value.serverTime >= exp) {
// 获取刷新 token
const refreshAccessToken = Local.get(refreshAccessTokenKey);
@@ -103,6 +108,9 @@ axiosInstance.interceptors.request.use(
// axios 响应拦截
axiosInstance.interceptors.response.use(
(res) => {
+ // 记录服务器时间
+ themeConfig.value.serverTime = res.data.time ? new Date(res.data.time) : undefined;
+
// 获取状态码和返回数据
var status = res.status;
var serve = res.data;
@@ -241,4 +249,4 @@ export function getJWTDate(timestamp: number): Date {
*/
export function sleep(delay: number) {
return new Promise((resolve) => setTimeout(resolve, delay));
-}
+}
\ No newline at end of file
diff --git a/Web/src/utils/sysInfo.ts b/Web/src/utils/sysInfo.ts
index 07919dee..ecff715a 100644
--- a/Web/src/utils/sysInfo.ts
+++ b/Web/src/utils/sysInfo.ts
@@ -65,6 +65,8 @@ export async function loadSysInfo(tenantid: number) {
themeConfig.value.onlineNotice = data.onlineNotice;
// 密码加解密公匙
window.__env__.VITE_SM_PUBLIC_KEY = data.publicKey;
+ // 服务器时间
+ themeConfig.value.serverTime = res.data.time ? new Date(Date.parse(res.data.time as any)) : undefined;
// 更新 favicon
updateFavicon(data.logo);