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