fix(auth/logout): 禁用内置 LogoutFilter,登出改走 auth 模块清会话 cookie

POST /logout 原先被 Spring Security 内置 LogoutFilter 截走(204),只清空
SecurityContext,从不执行 LoginController.logout,导致 aiimage_token cookie
残留、退出登录后仍保持登录态。SecurityConfig 显式 logout.disable() 后请求
落到 POST /logout → 200 + Set-Cookie aiimage_token=;(Max-Age=0) 清除。
补充真实登出 E2E(live):登出返回 200、下发清除 cookie、无 cookie 后 401。
注:此前的 500/CNFE 系运行进程 jar 被覆盖为 thin jar 所致,重建 fat jar
并重启即恢复;本提交只改逻辑。11/11 live 用例通过。
This commit is contained in:
2026-09-05 14:49:52 +08:00
parent 40224d8e56
commit 289f4488f9
2 changed files with 29 additions and 0 deletions
@@ -130,3 +130,30 @@ test('test_task_040_live_smoke_dependency_failure_returns_actionable_message', {
const user = parseCurrentUser(await (await authedFetch('/api/admin/current-user')).json())
assert.ok(roleKind(user.role) !== null)
})
test('test_task_040_live_smoke_logout_clears_cookie', { skip: !HAS_CREDS }, async () => {
// 真实登出 E2EPOST /logout 下发清除 aiimage_token 的 Set-Cookie;清除后同源请求回到 401。
const login = await fetch(`${BASE}/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: USER, password: PASS, deviceId: `${DEVICE}-e2e-logout` }),
})
const loginBody = (await login.json()) as { success?: boolean; data?: { token?: string } }
assert.equal(loginBody.success, true)
const token = loginBody.data?.token || ''
assert.ok(token)
const cookie = `aiimage_token=${token}`
const out = await fetch(`${BASE}/logout`, {
method: 'POST',
headers: { Cookie: cookie, 'X-Requested-With': 'XMLHttpRequest' },
})
assert.equal(out.status, 200, '登出应返回 200 而非内置 LogoutFilter 的 204/500')
assert.equal(((await out.json()) as { success?: boolean }).success, true)
const clearCookies = typeof out.headers.getSetCookie === 'function' ? out.headers.getSetCookie() : []
const clearHeader = clearCookies[0] || out.headers.get('set-cookie') || ''
assert.match(clearHeader, /aiimage_token=;/, '登出应清除 aiimage_token cookie')
assert.match(clearHeader, /Max-Age=0|Expires=Thu, 01 Jan 1970/)
const after = (await (await fetch(`${BASE}/api/admin/current-user`)).json()) as { success?: boolean; code?: number }
assert.equal(after.success, false)
assert.equal(after.code, 401)
})
@@ -19,6 +19,8 @@ public class SecurityConfig {
return http
.cors(Customizer.withDefaults())
.csrf(csrf -> csrf.disable())
// 登出走 auth 模块 POST /logout(清 aiimage_token cookie),禁用内置 LogoutFilter
.logout(logout -> logout.disable())
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
.httpBasic(Customizer.withDefaults())
.build();