feat: 将 Gateway Web 迁移到 BFF 会话

浏览器只通过 HttpOnly Cookie 恢复统一认证状态,不再访问认证中心 Token Endpoint 或保存 OIDC Token。同步更新错误提示、部署配置和接入文档。
This commit is contained in:
2026-07-13 19:09:57 +08:00
parent c5c82bb528
commit 46bac51703
15 changed files with 143 additions and 287 deletions
+18 -42
View File
@@ -50,7 +50,6 @@ import {
deleteApiKey,
deleteFileStorageChannel,
deleteGatewayUser,
deleteOIDCBrowserSession,
deletePlatform,
deleteTenant,
deleteUserGroup,
@@ -114,13 +113,11 @@ import { usePricingRuleSetOperations } from './hooks/usePricingRuleSetOperations
import { useRuntimePolicySetOperations } from './hooks/useRuntimePolicySetOperations';
import {
persistAccessToken,
persistLegacyOIDCAccessToken,
readLegacyOIDCAccessToken,
readStoredAccessToken,
} from './lib/auth-storage';
import { activateOIDCBrowserSession, restoreOIDCBrowserSession } from './lib/oidc-browser-session';
import { restoreOIDCBrowserSession } from './lib/oidc-browser-session';
import {
completeOIDCLogin,
consumeOIDCCallbackError,
oidcBrowserSessionEnabled,
oidcLoginEnabled,
startOIDCLogin,
@@ -254,6 +251,7 @@ export function App() {
const [coreMessage, setCoreMessage] = useState('');
const [state, setState] = useState<LoadState>('idle');
const [error, setError] = useState('');
const [oidcCallbackError, setOIDCCallbackError] = useState(() => consumeOIDCCallbackError());
const loadedDataKeysRef = useRef(new Set<DataKey>());
const loadingDataKeysRef = useRef(new Set<DataKey>());
const loadedTaskQueryKeyRef = useRef('');
@@ -288,35 +286,16 @@ export function App() {
useEffect(() => {
let cancelled = false;
void (async () => {
const result = await completeOIDCLogin();
if (cancelled) return;
if (result) {
if (!oidcBrowserSessionEnabled()) {
persistLegacyOIDCAccessToken(result.accessToken);
setToken(result.accessToken);
applyRoute(parseAppRoute(result.returnTo));
return;
}
const credential = await activateOIDCBrowserSession(result.accessToken);
if (cancelled) return;
setToken(credential);
applyRoute(parseAppRoute(result.returnTo));
return;
}
if (oidcCallbackError) return;
if (!oidcBrowserSessionEnabled()) return;
const legacyOIDCToken = readLegacyOIDCAccessToken();
if (legacyOIDCToken) {
const credential = await activateOIDCBrowserSession(legacyOIDCToken);
if (!cancelled) setToken(credential);
return;
}
if (readStoredAccessToken() || !oidcLoginEnabled()) return;
const restored = await restoreOIDCBrowserSession();
if (!restored || cancelled) return;
setCurrentUser(restored.user);
loadedDataKeysRef.current.add('currentUser');
setToken(restored.credential);
setState('idle');
setError('');
})().catch((err) => {
if (cancelled) return;
setState('error');
@@ -325,7 +304,10 @@ export function App() {
return () => {
cancelled = true;
};
}, []);
}, [oidcCallbackError]);
useEffect(() => {
if (token) setOIDCCallbackError('');
}, [token]);
useEffect(() => {
void ensureData(['health']);
}, []);
@@ -1150,33 +1132,27 @@ export function App() {
async function signOut() {
const wasOIDCSession = token === OIDC_BROWSER_SESSION_CREDENTIAL;
if (wasOIDCSession) {
try {
await deleteOIDCBrowserSession();
} catch (err) {
setState('error');
setError(err instanceof Error ? err.message : '统一认证会话注销失败,请重试');
return;
}
}
const shouldEndOIDCSession = wasOIDCSession || currentUser?.source === 'oidc';
resetAuthenticatedSession();
if (shouldEndOIDCSession) {
try {
if (await startOIDCLogout()) return;
if (await startOIDCLogout()) {
resetAuthenticatedSession();
return;
}
} catch {
navigatePath('/');
setState('error');
setError('Gateway 会话已注销,但统一认证退出失败');
setError('统一认证退出失败,请重试');
return;
}
}
resetAuthenticatedSession();
navigatePath('/');
}
function loginWithOIDC() {
setState('loading');
setError('');
setOIDCCallbackError('');
void startOIDCLogin().catch((err) => {
setState('error');
setError(err instanceof Error ? err.message : '统一认证登录失败');
@@ -1255,7 +1231,7 @@ export function App() {
onRefresh={() => void refresh()}
onSignOut={signOut}
>
{error && <div className="notice">{error}</div>}
{(oidcCallbackError || error) && <div className="notice">{oidcCallbackError || error}</div>}
{activePage === 'home' && <HomePage onNavigate={navigatePage} onPlaygroundMode={navigatePlaygroundMode} />}
{activePage === 'playground' && (
<PlaygroundPage