chore: 优化oidc登录

This commit is contained in:
xiaojunnuo
2025-11-28 01:42:42 +08:00
parent 2fabee647a
commit 21585ca565
17 changed files with 264 additions and 130 deletions
@@ -1,8 +1,8 @@
<template>
<div id="userLayout" :class="['user-layout-wrapper']">
<div class="login-container flex-center">
<div class="user-layout-content flex-center flex-col">
<div class="top flex flex-col items-center justify-center">
<div class="login-container flex justify-start">
<div class="user-layout-content flex-col justify-start">
<div class="top flex flex-col items-center justify-start">
<div class="header flex flex-row items-center">
<img :src="siteInfo.loginLogo" class="logo" alt="logo" />
<span class="title"></span>
@@ -10,8 +10,9 @@
<div class="desc">{{ siteInfo.slogan }}</div>
</div>
<router-view />
<div class="flex-1 flex flex-col justify-start items-center">
<router-view />
</div>
<div class="footer">
<div class="copyright">
<span v-if="!settingStore.isComm">
@@ -57,6 +57,7 @@ export default {
passwordPlaceholder: "Please enter your password",
mobilePlaceholder: "Please enter your mobile number",
loginButton: "Log In",
bindButton: "Bind Account",
forgotPassword: "Forgot password?",
forgotAdminPassword: "Forgot admin password?",
registerLink: "Register",
@@ -760,6 +760,13 @@ export default {
fixedCertExpireDays: "Fixed Cert Expire Days",
fixedCertExpireDaysHelper: "Fixed cert expiration days, helpful for table list progress bar display",
fixedCertExpireDaysRecommend: "Recommend 90",
enableOauth: "Enable OAuth2 Login",
oauthEnabledHelper: "Whether to enable OAuth2 login",
oauthProviders: "OAuth2 Login Providers",
oauthType: "OAuth2 Login Type",
oauthConfig: "OAuth2 Login Config",
oauthProviderSelectorPlaceholder: "Please select OAuth2 login provider",
},
},
modal: {
@@ -57,6 +57,7 @@ export default {
passwordPlaceholder: "请输入密码",
mobilePlaceholder: "请输入手机号",
loginButton: "登录",
bindButton: "绑定账号",
forgotPassword: "忘记密码?",
forgotAdminPassword: "忘记管理员密码?",
registerLink: "注册",
@@ -761,6 +761,13 @@ export default {
fixedCertExpireDays: "固定证书有效期天数",
fixedCertExpireDaysHelper: "固定证书有效期天数,有助于列表进度条整齐显示",
fixedCertExpireDaysRecommend: "推荐90",
enableOauth: "启用OAuth2登录",
oauthEnabledHelper: "是否启用OAuth2登录",
oauthProviders: "OAuth2登录提供商",
oauthType: "OAuth2登录类型",
oauthConfig: "OAuth2登录配置",
oauthProviderSelectorPlaceholder: "请选择OAuth2登录提供商",
},
},
modal: {
@@ -48,28 +48,26 @@
</a-tabs>
<a-form-item>
<a-button type="primary" size="large" html-type="button" :loading="loading" class="login-button" @click="handleFinish">
{{ t("authentication.loginButton") }}
{{ queryBindCode ? t("authentication.bindButton") : t("authentication.loginButton") }}
</a-button>
<div v-if="!!settingStore.sysPublic.selfServicePasswordRetrievalEnabled && !queryBindCode" class="mt-2">
<router-link :to="{ name: 'forgotPassword' }">
{{ t("authentication.forgotPassword") }}
</router-link>
</div>
</a-form-item>
<div class="mt-2 flex justify-between items-center">
<div class="flex items-center gap-2">
<language-toggle class="text-blue-500"></language-toggle>
<router-link v-if="!!settingStore.sysPublic.selfServicePasswordRetrievalEnabled && !queryBindCode" :to="{ name: 'forgotPassword' }">
{{ t("authentication.forgotPassword") }}
</router-link>
</div>
<a-form-item class="user-login-other">
<div class="flex flex-between justify-between items-center">
<language-toggle class="color-blue"></language-toggle>
<router-link v-if="hasRegisterTypeEnabled() && !queryBindCode" class="register" :to="{ name: 'register' }">
{{ t("authentication.registerLink") }}
</router-link>
</div>
<div class="flex flex-between justify-between items-center mt-5">
<oauth-footer></oauth-footer>
</div>
</a-form-item>
<div v-if="!queryBindCode" class="w-full">
<oauth-footer></oauth-footer>
</div>
</a-form>
<a-form v-else ref="twoFactorFormRef" class="user-layout-login" :model="twoFactor" v-bind="layout">
<div class="mb-10 flex flex-center">请打开您的Authenticator APP获取动态验证码</div>
@@ -12,13 +12,13 @@ export async function OauthLogin(type: string) {
});
}
export async function OauthCallback(type: string, query: Record<string, string>) {
export async function OauthToken(type: string, validationCode: string) {
return await request({
url: apiPrefix + `/callback`,
url: apiPrefix + `/token`,
method: "post",
data: {
type,
...query,
validationCode,
},
});
}
@@ -43,3 +43,10 @@ export async function BindUser(code: string) {
},
});
}
export async function GetOauthProviders() {
return await request({
url: apiPrefix + "/providers",
method: "post",
});
}
@@ -2,7 +2,8 @@
<div class="oauth-callback-page">
<div class="oauth-callback-content">
<div v-if="!bindRequired" class="oauth-callback-title">
<span>登录中...</span>
<span v-if="!error">登录中...</span>
<span v-else>{{ error }}</span>
</div>
<div v-else class="oauth-callback-title">
<div>第三方登录成功还未绑定账号请选择</div>
@@ -29,17 +30,16 @@ import { useUserStore } from "/@/store/user";
const route = useRoute();
const router = useRouter();
const oauthType = route.params.type as string;
const query = route.query as Record<string, string>;
const validationCode = route.query.validationCode as string;
const error = ref(route.query.error as string);
const userStore = useUserStore();
const bindRequired = ref(false);
const bindCode = ref("");
async function handleOauthCallback() {
async function handleOauthToken() {
//处理第三方登录回调
const res = await api.OauthCallback(oauthType, query);
const res = await api.OauthToken(oauthType, validationCode);
if (res.token) {
//登录成功
userStore.onLoginSuccess(res);
@@ -55,7 +55,10 @@ async function handleOauthCallback() {
}
onMounted(async () => {
await handleOauthCallback();
if (error.value) {
return;
}
await handleOauthToken();
});
async function goBindUser() {
@@ -95,6 +98,7 @@ async function autoRegister() {
width: 500px;
margin: 0 auto;
margin-top: 50px;
margin-bottom: 100px;
.oauth-callback-title {
font-size: 24px;
@@ -1,24 +1,25 @@
<template>
<div class="oauth-footer">
<div class="oauth-footer relative">
<div class="oauth-title">
<div class="oauth-title-text">其他方式登录</div>
</div>
<div v-for="item in oauthList" :key="item.type">
<div class="oauth-icon-button pointer" @click="goOauthLogin(item.type)">
<el-icon :icon="item.icon" />
<span>{{ item.name }}</span>
<div class="oauth-icon-button pointer" @click="goOauthLogin(item.name)">
<div><fs-icon :icon="item.icon" class="text-blue-600 text-40" /></div>
<div>{{ item.title }}</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { onMounted, ref } from "vue";
import * as api from "./api";
const oauthList = ref([
{
name: "OIDC",
type: "oidc",
icon: "ion:oidc",
},
]);
const oauthList = ref([]);
onMounted(async () => {
oauthList.value = await api.GetOauthProviders();
});
async function goOauthLogin(type: string) {
//获取第三方登录URL
@@ -29,17 +30,56 @@ async function goOauthLogin(type: string) {
</script>
<style lang="less">
.oauth-footer {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 16px;
.oauth-title {
width: 100%;
font-size: 14px;
font-weight: 500;
color: #8c8c8c;
position: relative;
.oauth-title-text {
position: relative;
z-index: 1;
text-align: center;
&::after {
content: "";
position: absolute;
top: 50%;
left: 0;
width: 36%;
height: 0.5px;
background-color: #8c8c8c;
}
&::before {
content: "";
position: absolute;
top: 50%;
right: 0;
width: 36%;
height: 0.5px;
background-color: #8c8c8c;
}
}
}
.oauth-icon-button {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
padding: 8px 16px;
padding: 8px 8px;
border-radius: 100px;
.fs-icon {
font-size: 36px;
color: #006be6 !important;
}
}
}
</style>
@@ -66,7 +66,7 @@ function onChange(value: string) {
<style lang="less">
.page-sys-settings {
.sys-settings-form {
width: 800px;
width: 900px;
max-width: 100%;
padding: 20px;
}
@@ -54,30 +54,34 @@
<div class="helper">{{ t("certd.saveThenTest") }}</div>
</a-form-item>
</template>
<a-form-item :label="t('certd.enableOauth')" :name="['public', 'oauthEnabled']">
<a-form-item :label="t('certd.sys.setting.enableOauth')" :name="['public', 'oauthEnabled']">
<div class="flex-o">
<a-switch v-model:checked="formState.public.oauthEnabled" :disabled="!settingsStore.isPlus" :title="t('certd.plusFeature')" />
<vip-button class="ml-5" mode="plus"></vip-button>
<vip-button class="ml-5" mode="button"></vip-button>
</div>
</a-form-item>
<a-form-item v-if="formState.public.oauthEnabled" :label="t('certd.oauthProviders')" :name="['public', 'oauthProviders']">
<a-form-item v-if="formState.public.oauthEnabled" :label="t('certd.sys.setting.oauthProviders')" :name="['public', 'oauthProviders']">
<div class="flex flex-wrap">
<table>
<tr>
<th>{{ t("certd.oauthType") }}</th>
<th>{{ t("certd.oauthConfig") }}</th>
</tr>
<tr v-for="(item, key) of oauthProviders" :key="key">
<td>
<div class="flex items-center">
<fs-icon :icon="item.icon" />
{{ item.title }}
</div>
</td>
<td>
<AddonSelector v-model:model-value="item.addonId" addon-type="oauth" from="sys" :type="item.name" :placeholder="t('certd.clientIdPlaceholder')" />
</td>
</tr>
<table class="w-full table-auto border-collapse border border-gray-400">
<thead>
<tr>
<th class="border border-gray-300 px-4 py-2 w-1/2">{{ t("certd.sys.setting.oauthType") }}</th>
<th class="border border-gray-300 px-4 py-2 w-1/2">{{ t("certd.sys.setting.oauthConfig") }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, key) of oauthProviders" :key="key">
<td class="border border-gray-300 px-4 py-2">
<div class="flex items-center" :title="item.desc">
<fs-icon :icon="item.icon" class="mr-2 text-blue-600" />
{{ item.title }}
</div>
</td>
<td class="border border-gray-300 px-4 py-2">
<AddonSelector v-model:model-value="item.addonId" addon-type="oauth" from="sys" :type="item.name" :placeholder="t('certd.sys.setting.oauthProviderSelectorPlaceholder')" />
</td>
</tr>
</tbody>
</table>
</div>
</a-form-item>
@@ -190,7 +194,6 @@ async function loadOauthProviders() {
let list: any = await api.GetOauthProviders();
oauthProviders.value = list;
for (const item of list) {
debugger;
const type = item.name;
const provider = formState.public.oauthProviders?.[type];
if (provider) {