From cafa8bc840ba9a7f49fed7d69f31ce685cc907d1 Mon Sep 17 00:00:00 2001 From: mac Date: Thu, 16 Jul 2026 16:19:25 +0800 Subject: [PATCH] feat(auth): support local username login mode --- frontend/src/App.vue | 8 ++- frontend/src/api/auth.ts | 45 +++++++++++++++-- frontend/src/api/request.ts | 11 +++- frontend/src/router/index.ts | 9 ++++ frontend/src/views/auth/Login.vue | 71 +++++++++++++++++++++++--- server/README.md | 12 ++--- server/internal/config/config.go | 2 + server/internal/handler/auth.go | 78 ++++++++++++++++++++++++++++ server/internal/router/router.go | 3 ++ server/internal/service/auth.go | 84 +++++++++++++++++++++++++++++++ 10 files changed, 304 insertions(+), 19 deletions(-) create mode 100644 server/internal/handler/auth.go diff --git a/frontend/src/App.vue b/frontend/src/App.vue index c1dd884..c99d628 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -4,6 +4,7 @@ diff --git a/frontend/src/api/auth.ts b/frontend/src/api/auth.ts index 2317520..2b9cf17 100644 --- a/frontend/src/api/auth.ts +++ b/frontend/src/api/auth.ts @@ -3,7 +3,7 @@ import { getToken } from '@/utils/auth' export interface LoginRequest { username: string - password: string + password?: string } export interface UserInfo { @@ -20,9 +20,48 @@ export interface LoginResponse { user: UserInfo } +export interface AuthConfig { + sso_enabled: boolean +} + export const authApi = { - login(_data: LoginRequest): Promise> { - return Promise.reject(new Error('password login is disabled')) + async getConfig(): Promise> { + const response = await fetch('/auth/api/v1/config', { + headers: { + Accept: 'application/json', + }, + }) + const data = await response.json().catch(() => ({})) + if (!response.ok) { + throw new Error(data.error || `HTTP ${response.status}`) + } + return { + code: 0, + message: 'success', + data: { + sso_enabled: data.sso_enabled !== false, + }, + } + }, + + async login(data: LoginRequest): Promise> { + const response = await fetch('/auth/api/v1/login', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ username: data.username }), + }) + const result = await response.json().catch(() => ({})) + if (!response.ok) { + throw new Error(result.error || `HTTP ${response.status}`) + } + return { + code: 0, + message: 'success', + data: result, + } }, async logout(): Promise> { diff --git a/frontend/src/api/request.ts b/frontend/src/api/request.ts index c7fa184..6a6f390 100644 --- a/frontend/src/api/request.ts +++ b/frontend/src/api/request.ts @@ -61,7 +61,16 @@ request.interceptors.response.use( if (status === 401) { removeToken() - redirectToSSO() + fetch('/auth/api/v1/config', { headers: { Accept: 'application/json' } }) + .then((response) => response.json()) + .then((data) => { + if (data.sso_enabled === false) { + window.location.assign('/login') + } else { + redirectToSSO() + } + }) + .catch(() => redirectToSSO()) ElMessage.error(backendMessage || '未授权,请重新登录') } else { const message = backendMessage || httpMessageMap[status] || `请求失败 (${status})` diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index cdb310f..e7e74ed 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -2,6 +2,7 @@ import { createRouter, createWebHistory } from 'vue-router' import { getToken } from '@/utils/auth' import { consumeSSOToken, redirectToSSO } from '@/utils/sso' import { useAuthStore } from '@/stores/auth' +import { authApi } from '@/api/auth' const router = createRouter({ history: createWebHistory(), @@ -113,6 +114,10 @@ router.beforeEach(async (to) => { const token = authStore.token || getToken() if (to.meta.requiresAuth !== false && !token) { + const { data } = await authApi.getConfig() + if (!data.sso_enabled) { + return { path: '/login', query: to.fullPath === '/' ? {} : { redirect: to.fullPath } } + } redirectToSSO() return false } @@ -121,6 +126,10 @@ router.beforeEach(async (to) => { await authStore.refreshUser() } catch { authStore.clearAuth() + const { data } = await authApi.getConfig() + if (!data.sso_enabled) { + return { path: '/login', query: to.fullPath === '/' ? {} : { redirect: to.fullPath } } + } redirectToSSO() return false } diff --git a/frontend/src/views/auth/Login.vue b/frontend/src/views/auth/Login.vue index 2d39019..bc08c66 100644 --- a/frontend/src/views/auth/Login.vue +++ b/frontend/src/views/auth/Login.vue @@ -9,32 +9,83 @@

{{ title }}

{{ subtitle }}

-