mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-10-31 02:08:45 +08:00 
			
		
		
		
	完善SSO单点登录
This commit is contained in:
		| @@ -76,7 +76,14 @@ export const reqCheckApi = (data) => { | |||||||
| } | } | ||||||
|  |  | ||||||
| // ========== OAUTH 2.0 相关 ========== | // ========== OAUTH 2.0 相关 ========== | ||||||
|  | export type scopesType = string[] | ||||||
|  | export interface paramsType { | ||||||
|  |   responseType: string | ||||||
|  |   clientId: string | ||||||
|  |   redirectUri: string | ||||||
|  |   state: string | ||||||
|  |   scopes: scopesType | ||||||
|  | } | ||||||
| export const getAuthorize = (clientId) => { | export const getAuthorize = (clientId) => { | ||||||
|   return request.get({ url: '/system/oauth2/authorize?clientId=' + clientId }) |   return request.get({ url: '/system/oauth2/authorize?clientId=' + clientId }) | ||||||
| } | } | ||||||
| @@ -87,8 +94,8 @@ export function authorize( | |||||||
|   redirectUri: string, |   redirectUri: string, | ||||||
|   state: string, |   state: string, | ||||||
|   autoApprove: boolean, |   autoApprove: boolean, | ||||||
|   checkedScopes: any, |   checkedScopes: scopesType, | ||||||
|   uncheckedScopes: any |   uncheckedScopes: scopesType | ||||||
| ) { | ) { | ||||||
|   // 构建 scopes |   // 构建 scopes | ||||||
|   const scopes = {} |   const scopes = {} | ||||||
|   | |||||||
| @@ -9,19 +9,19 @@ | |||||||
|       > |       > | ||||||
|         <!-- 左上角的 logo + 系统标题 --> |         <!-- 左上角的 logo + 系统标题 --> | ||||||
|         <div class="flex items-center relative text-white"> |         <div class="flex items-center relative text-white"> | ||||||
|           <img src="@/assets/imgs/logo.png" alt="" class="w-48px h-48px mr-10px" /> |           <img alt="" class="w-48px h-48px mr-10px" src="@/assets/imgs/logo.png" /> | ||||||
|           <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span> |           <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span> | ||||||
|         </div> |         </div> | ||||||
|         <!-- 左边的背景图 + 欢迎语 --> |         <!-- 左边的背景图 + 欢迎语 --> | ||||||
|         <div class="flex justify-center items-center h-[calc(100%-60px)]"> |         <div class="flex justify-center items-center h-[calc(100%-60px)]"> | ||||||
|           <TransitionGroup |           <TransitionGroup | ||||||
|             appear |             appear | ||||||
|             tag="div" |  | ||||||
|             enter-active-class="animate__animated animate__bounceInLeft" |             enter-active-class="animate__animated animate__bounceInLeft" | ||||||
|  |             tag="div" | ||||||
|           > |           > | ||||||
|             <img src="@/assets/svgs/login-box-bg.svg" key="1" alt="" class="w-350px" /> |             <img key="1" alt="" class="w-350px" src="@/assets/svgs/login-box-bg.svg" /> | ||||||
|             <div class="text-3xl text-white" key="2">{{ t('login.welcome') }}</div> |             <div key="2" class="text-3xl text-white">{{ t('login.welcome') }}</div> | ||||||
|             <div class="mt-5 font-normal text-white text-14px" key="3"> |             <div key="3" class="mt-5 font-normal text-white text-14px"> | ||||||
|               {{ t('login.message') }} |               {{ t('login.message') }} | ||||||
|             </div> |             </div> | ||||||
|           </TransitionGroup> |           </TransitionGroup> | ||||||
| @@ -31,7 +31,7 @@ | |||||||
|         <!-- 右上角的主题、语言选择 --> |         <!-- 右上角的主题、语言选择 --> | ||||||
|         <div class="flex justify-between items-center text-white @2xl:justify-end @xl:justify-end"> |         <div class="flex justify-between items-center text-white @2xl:justify-end @xl:justify-end"> | ||||||
|           <div class="flex items-center @2xl:hidden @xl:hidden"> |           <div class="flex items-center @2xl:hidden @xl:hidden"> | ||||||
|             <img src="@/assets/imgs/logo.png" alt="" class="w-48px h-48px mr-10px" /> |             <img alt="" class="w-48px h-48px mr-10px" src="@/assets/imgs/logo.png" /> | ||||||
|             <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span> |             <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span> | ||||||
|           </div> |           </div> | ||||||
|           <div class="flex justify-end items-center space-x-10px"> |           <div class="flex justify-end items-center space-x-10px"> | ||||||
| @@ -52,18 +52,15 @@ | |||||||
|             <QrCodeForm class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" /> |             <QrCodeForm class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" /> | ||||||
|             <!-- 注册 --> |             <!-- 注册 --> | ||||||
|             <RegisterForm class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" /> |             <RegisterForm class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" /> | ||||||
|             <!-- 三方登录 v-if触发组件初始化 --> |             <!-- 三方登录 --> | ||||||
|             <SSOLoginVue |             <SSOLoginVue class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" /> | ||||||
|               v-if="isSSO" |  | ||||||
|               class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" |  | ||||||
|             /> |  | ||||||
|           </div> |           </div> | ||||||
|         </Transition> |         </Transition> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| <script setup lang="ts"> | <script lang="ts" setup> | ||||||
| import { underlineToHump } from '@/utils' | import { underlineToHump } from '@/utils' | ||||||
|  |  | ||||||
| import { useDesign } from '@/hooks/web/useDesign' | import { useDesign } from '@/hooks/web/useDesign' | ||||||
| @@ -72,23 +69,11 @@ import { ThemeSwitch } from '@/layout/components/ThemeSwitch' | |||||||
| import { LocaleDropdown } from '@/layout/components/LocaleDropdown' | import { LocaleDropdown } from '@/layout/components/LocaleDropdown' | ||||||
|  |  | ||||||
| import { LoginForm, MobileForm, QrCodeForm, RegisterForm, SSOLoginVue } from './components' | import { LoginForm, MobileForm, QrCodeForm, RegisterForm, SSOLoginVue } from './components' | ||||||
| import { RouteLocationNormalizedLoaded } from 'vue-router' |  | ||||||
|  |  | ||||||
| const { t } = useI18n() | const { t } = useI18n() | ||||||
| const appStore = useAppStore() | const appStore = useAppStore() | ||||||
| const { getPrefixCls } = useDesign() | const { getPrefixCls } = useDesign() | ||||||
| const prefixCls = getPrefixCls('login') | const prefixCls = getPrefixCls('login') | ||||||
| // =======SSO====== |  | ||||||
| const isSSO = ref(false) |  | ||||||
| const router = useRouter() |  | ||||||
| // 监听当前路由 |  | ||||||
| watch( |  | ||||||
|   () => router.currentRoute.value, |  | ||||||
|   (route: RouteLocationNormalizedLoaded) => { |  | ||||||
|     if (route.name === 'SSOLogin') isSSO.value = true |  | ||||||
|   }, |  | ||||||
|   { immediate: true } |  | ||||||
| ) |  | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
|   | |||||||
| @@ -279,7 +279,6 @@ const doSocialLogin = async (type: number) => { | |||||||
| watch( | watch( | ||||||
|   () => currentRoute.value, |   () => currentRoute.value, | ||||||
|   (route: RouteLocationNormalizedLoaded) => { |   (route: RouteLocationNormalizedLoaded) => { | ||||||
|     if (route.name === 'SSOLogin') setLoginState(LoginStateEnum.SSO) |  | ||||||
|     redirect.value = route?.query?.redirect as string |     redirect.value = route?.query?.redirect as string | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| <template> | <template> | ||||||
|   <!-- 表单 --> |   <!-- 表单 --> | ||||||
|   <div class="form-cont"> |   <div v-show="getShow" class="form-cont"> | ||||||
|  |     <!--    <LoginFormTitle style="width: 100%" />--> | ||||||
|     <el-tabs class="form" style="float: none" value="uname"> |     <el-tabs class="form" style="float: none" value="uname"> | ||||||
|       <el-tab-pane :label="'三方授权(' + client.name + ')'" name="uname" /> |       <el-tab-pane :label="'三方授权(' + client.name + ')'" name="uname" /> | ||||||
|     </el-tabs> |     </el-tabs> | ||||||
| @@ -12,8 +13,8 @@ | |||||||
|           <el-checkbox-group v-model="loginForm.scopes"> |           <el-checkbox-group v-model="loginForm.scopes"> | ||||||
|             <el-checkbox |             <el-checkbox | ||||||
|               v-for="scope in params.scopes" |               v-for="scope in params.scopes" | ||||||
|               :label="scope" |  | ||||||
|               :key="scope" |               :key="scope" | ||||||
|  |               :label="scope" | ||||||
|               style="display: block; margin-bottom: -10px" |               style="display: block; margin-bottom: -10px" | ||||||
|               >{{ formatScope(scope) }} |               >{{ formatScope(scope) }} | ||||||
|             </el-checkbox> |             </el-checkbox> | ||||||
| @@ -24,8 +25,8 @@ | |||||||
|           <el-button |           <el-button | ||||||
|             :loading="loading" |             :loading="loading" | ||||||
|             size="small" |             size="small" | ||||||
|             type="primary" |  | ||||||
|             style="width: 60%" |             style="width: 60%" | ||||||
|  |             type="primary" | ||||||
|             @click.prevent="handleAuthorize(true)" |             @click.prevent="handleAuthorize(true)" | ||||||
|           > |           > | ||||||
|             <span v-if="!loading">同意授权</span> |             <span v-if="!loading">同意授权</span> | ||||||
| @@ -40,19 +41,15 @@ | |||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| <script lang="ts" name="SSOLogin" setup> | <script lang="ts" name="SSOLogin" setup> | ||||||
| import { authorize, getAuthorize } from '@/api/login' | // import LoginFormTitle from './LoginFormTitle.vue' // TODO 艿艿你看看要不要这个表头 | ||||||
|  | import { authorize, getAuthorize, paramsType, scopesType } from '@/api/login' | ||||||
|  | import { LoginStateEnum, useLoginState } from './useLogin' | ||||||
|  | import type { RouteLocationNormalizedLoaded } from 'vue-router' | ||||||
|  |  | ||||||
| const { t } = useI18n() | const { t } = useI18n() | ||||||
| const ssoForm = ref() // 表单Ref | const ssoForm = ref() // 表单Ref | ||||||
|  | const { getLoginState, setLoginState } = useLoginState() | ||||||
| type scopesType = string[] | const getShow = computed(() => unref(getLoginState) === LoginStateEnum.SSO) | ||||||
| interface paramsType { |  | ||||||
|   responseType: string |  | ||||||
|   clientId: string |  | ||||||
|   redirectUri: string |  | ||||||
|   state: string |  | ||||||
|   scopes: scopesType |  | ||||||
| } |  | ||||||
| const loginForm = reactive<{ scopes: scopesType }>({ | const loginForm = reactive<{ scopes: scopesType }>({ | ||||||
|   scopes: [] // 已选中的 scope 数组 |   scopes: [] // 已选中的 scope 数组 | ||||||
| }) | }) | ||||||
| @@ -116,6 +113,7 @@ const doAuthorize = (autoApprove, checkedScopes, uncheckedScopes) => { | |||||||
| const formatScope = (scope) => { | const formatScope = (scope) => { | ||||||
|   // 格式化 scope 授权范围,方便用户理解。 |   // 格式化 scope 授权范围,方便用户理解。 | ||||||
|   // 这里仅仅是一个 demo,可以考虑录入到字典数据中,例如说字典类型 "system_oauth2_scope",它的每个 scope 都是一条字典数据。 |   // 这里仅仅是一个 demo,可以考虑录入到字典数据中,例如说字典类型 "system_oauth2_scope",它的每个 scope 都是一条字典数据。 | ||||||
|  |   // TODO 这个之做了中文部分 | ||||||
|   return t(`login.sso.${scope}`) |   return t(`login.sso.${scope}`) | ||||||
| } | } | ||||||
| const route = useRoute() | const route = useRoute() | ||||||
| @@ -146,7 +144,6 @@ const init = () => { | |||||||
|  |  | ||||||
|   // 获取授权页的基本信息 |   // 获取授权页的基本信息 | ||||||
|   getAuthorize(params.clientId).then((res) => { |   getAuthorize(params.clientId).then((res) => { | ||||||
|     console.log(res) |  | ||||||
|     client.value = res.client |     client.value = res.client | ||||||
|     // 解析 scope |     // 解析 scope | ||||||
|     let scopes |     let scopes | ||||||
| @@ -173,5 +170,18 @@ const init = () => { | |||||||
|     } |     } | ||||||
|   }) |   }) | ||||||
| } | } | ||||||
|  | // =======SSO====== | ||||||
|  | const { currentRoute } = useRouter() | ||||||
|  | // 监听当前路由 | ||||||
|  | watch( | ||||||
|  |   () => currentRoute.value, | ||||||
|  |   (route: RouteLocationNormalizedLoaded) => { | ||||||
|  |     if (route.name === 'SSOLogin') { | ||||||
|  |       setLoginState(LoginStateEnum.SSO) | ||||||
|  |       init() | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { immediate: true } | ||||||
|  | ) | ||||||
| init() | init() | ||||||
| </script> | </script> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 puhui999
					puhui999