fix(security): 文件列表 userId 隔离 + CAS 退出清 SSO

研读模块 /gpt/file/list 之前没强制按 userId 过滤,理论上枚举
knowledgeBaseId 能拿到他人文件元信息(虽然 download 那一步有 userId 校验
所以下不下来,但文件名/大小/上传时间会泄露)。

- FileController.listFile 强制注入 userId = getSysUserId()
- UploadFileMapper.xml BaseSelect 加 <if userId != null> 过滤分支

CAS 退出登录之前只清了本地 JWT,没调 CAS server logout,导致:
- 后端 Redis 里的 token 还在
- CAS server 的 SSO cookie 还在 → 再点"统一身份登录"立即静默登入
- 其他接 CAS 的系统也还能继续访问

新增 POST /app/api/logout:
- 删 Redis 里 LOGIN_TOKEN_KEY:{userId}:{sessionId}
- SecurityContextHolder.clearContext()
- 返回 casLogoutUrl(${serverLogout}?service=前端 login 页)

前端 Operates.vue quit() 改 async:先调 logout 拿 casLogoutUrl,
removeToken 后 window.location.href 跳过去,让 CAS 清 SSO cookie 再回 /login。

CasProperties 加回 serverLogout 字段(之前清理时删了,本次需要)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-07 20:34:48 +08:00
parent b83c540018
commit a548425923
7 changed files with 79 additions and 2 deletions

View File

@@ -16,6 +16,13 @@ export function fetchVerify<T>(data: object) {
})
}
// 退出登录:清后端 Redis 里的 token返回 CAS server logout URL前端跳过去清 SSO cookie
export function fetchLogout<T>() {
return post<T>({
url: '/app/api/logout',
})
}
// 获取用户信息
export function fetchSession<T>() {
return get<T>({

View File

@@ -57,6 +57,7 @@ import { useAuthStore } from "@/store";
import { reactive, watch, computed } from "vue";
import { useRouter, useRoute } from "vue-router";
import { User, Setting, SwitchButton, TopRight } from "@element-plus/icons-vue";
import { fetchLogout } from "@/api";
const authStore = useAuthStore();
const isAdmin = computed(() => authStore.session?.admind === true);
@@ -112,9 +113,22 @@ const openExtLink = (url: string) => {
window.open(url, '_blank');
};
const quit = () => {
const quit = async () => {
// 调后端 logout清 Redis token + 拿 CAS 服务器登出 URL
// 拿到后跳过去清 CAS SSO cookie否则下次"统一身份登录"会因为 cookie 还在直接静默登入
let casLogoutUrl: string | null = null;
try {
const res: any = await fetchLogout();
casLogoutUrl = res?.data?.casLogoutUrl || null;
} catch (e) {
console.warn("logout API 调用失败,本地清理后跳登录页", e);
}
authStore.removeToken();
router.push("/login");
if (casLogoutUrl) {
window.location.href = casLogoutUrl;
} else {
router.push("/login");
}
};
const goProfile = () => {