(null);
+ const [authMode, setAuthMode] = useState<'SSO' | 'JWT' | 'NONE'>('NONE');
+ const [userLoading, setUserLoading] = useState(true);
const handleLogin = async (values: { username: string; password: string }) => {
try {
@@ -69,10 +88,29 @@ export function Header() {
};
const handleLogout = () => {
- localStorage.removeItem('session');
- message.success(t('user.messages.logoutSuccess'));
- // Refresh the page after logout
- window.location.reload();
+ if (authMode === 'SSO') {
+ // SSO 模式:检查是否配置了有效的登出 URL
+ const logoutUrl = OMS_LOGOUT_URL;
+
+ // 如果配置的是默认值(内部 service 名称),只清除本地状态
+ if (logoutUrl.includes('oms-service') || logoutUrl.includes('localhost')) {
+ console.warn('OMS logout URL not configured or using internal address, skipping redirect');
+ setCurrentUser(null);
+ setAuthMode('NONE');
+ message.success(t('user.messages.logoutSuccess'));
+ window.location.reload();
+ } else {
+ // 使用配置的登出 URL
+ window.location.href = `${logoutUrl}?redirect=${encodeURIComponent(window.location.href)}`;
+ }
+ } else {
+ // JWT 模式:清除本地 session
+ localStorage.removeItem('session');
+ setCurrentUser(null);
+ setAuthMode('NONE');
+ message.success(t('user.messages.logoutSuccess'));
+ window.location.reload();
+ }
};
const openLoginDialog = () => {
@@ -83,12 +121,47 @@ export function Header() {
setSignupOpen(true);
};
+ // 检测是否在 ME 环境
+ const isSSOAvailable = () => {
+ const hostname = window.location.hostname;
+ // 通过域名或注入的全局变量判断
+ return hostname.includes('modelengine') ||
+ hostname.includes('me-platform') ||
+ (window as any).__ME_ENV__ === true;
+ };
+
+ // 获取当前用户信息(支持双模式)
useEffect(() => {
- window.addEventListener('show-login', openLoginDialog);
+ const fetchCurrentUser = async () => {
+ try {
+ const response = await getCurrentUser();
+ if (response.data) {
+ setCurrentUser(response.data);
+ setAuthMode(response.data.authMode);
- return () => {
- window.removeEventListener('show-login', openLoginDialog);
+ // 如果未登录,根据模式处理
+ if (!response.data.authenticated) {
+ if (isSSOAvailable()) {
+ // SSO 模式:自动跳转到 ME 登录
+ console.log('SSO mode detected, redirecting to ME login...');
+ // 不自动跳转,等待用户点击登录按钮
+ } else {
+ // JWT 模式:保持未登录状态
+ console.log('JWT mode, waiting for user to login');
+ }
+ } else {
+ console.log(`User authenticated via ${response.data.authMode}:`, response.data.username);
+ }
+ }
+ } catch (error) {
+ console.error('Failed to fetch current user:', error);
+ // 请求失败时,保持未登录状态
+ } finally {
+ setUserLoading(false);
+ }
};
+
+ fetchCurrentUser();
}, []);
const languageMenuItems: MenuProps['items'] = [
@@ -110,28 +183,68 @@ export function Header() {
}
]
- const userDropdownItems: MenuProps['items'] = localStorage.getItem("session")
+ const userDropdownItems: MenuProps['items'] = currentUser?.authenticated
? [
- {
- key: 'profile',
- label: JSON.parse(localStorage.getItem("session") as string).email,
- },
- {
- type: 'divider',
- },
- {
- key: 'logout',
- label: t('user.actions.logout'),
- icon: ,
- onClick: handleLogout,
- },
- ]
+ {
+ key: 'profile',
+ label: (
+
+ {currentUser.username}
+
+ {authMode === 'SSO' ? (
+
+
+ {t('user.authMode.sso')}
+
+ ) : (
+
+
+ {t('user.authMode.jwt')}
+
+ )}
+
+
+ ),
+ },
+ currentUser.groupId && {
+ key: 'groupId',
+ label: `${t('user.group')}: ${currentUser.groupId}`,
+ disabled: true,
+ },
+ currentUser.email && {
+ key: 'email',
+ label: currentUser.email,
+ disabled: true,
+ },
+ // 只有 JWT 模式才显示退出登录按钮
+ ...(authMode !== 'SSO' ? [
+ {
+ type: 'divider',
+ },
+ {
+ key: 'logout',
+ label: t('user.actions.logout'),
+ icon: ,
+ onClick: handleLogout,
+ },
+ ] : []),
+ ]
: [
{
key: 'login',
- label: t('user.actions.login'),
+ label: authMode === 'SSO' || isSSOAvailable()
+ ? t('user.actions.gotoLogin')
+ : t('user.actions.login'),
icon: ,
- onClick: () => setLoginOpen(true),
+ onClick: () => {
+ if (authMode === 'SSO' || isSSOAvailable()) {
+ // SSO 模式:跳转到 ME 登录
+ window.location.href = `${ME_LOGIN_URL}?redirect=${encodeURIComponent(window.location.href)}`;
+ } else {
+ // JWT 模式:显示登录对话框
+ setLoginOpen(true);
+ }
+ },
},
{
type: 'divider',
@@ -140,7 +253,13 @@ export function Header() {
key: 'register',
label: t('user.actions.register'),
icon: ,
- onClick: () => setSignupOpen(true),
+ onClick: () => {
+ if (authMode === 'SSO' || isSSOAvailable()) {
+ message.info(t('user.messages.useSSO'));
+ } else {
+ setSignupOpen(true);
+ }
+ },
},
];
@@ -175,10 +294,10 @@ export function Header() {
placement="bottomRight"
overlayClassName="w-40"
>
- }>
- {localStorage.getItem("session") ? (
- {JSON.parse(localStorage.getItem("session") as string).username}
- ) : null}
+ } loading={userLoading}>
+ {currentUser?.authenticated && !userLoading && (
+ {currentUser.username}
+ )}
diff --git a/frontend/src/utils/request.ts b/frontend/src/utils/request.ts
index ef92fef93..3de3b4735 100644
--- a/frontend/src/utils/request.ts
+++ b/frontend/src/utils/request.ts
@@ -280,6 +280,8 @@ class Request {
const config = {
method: "GET",
+ credentials: "include",
+ mode: "cors",
headers: {
...this.defaultHeaders,
...options.headers,
@@ -299,6 +301,8 @@ class Request {
async post(url, data = {}, options = {}) {
let config = {
method: "POST",
+ credentials: "include",
+ mode: "cors",
headers: {
...this.defaultHeaders,
...options.headers,
@@ -311,6 +315,8 @@ class Request {
if (isFormData) {
config = {
method: "POST",
+ credentials: "include",
+ mode: "cors",
headers: {
...options.headers, // FormData不需要Content-Type
},
@@ -330,6 +336,8 @@ class Request {
async put(url, data = null, options = {}) {
const config = {
method: "PUT",
+ credentials: "include",
+ mode: "cors",
headers: {
...this.defaultHeaders,
...options.headers,