Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
0f0ba8b8
authored
Apr 08, 2023
by
芋道源码
Committed by
Gitee
Apr 08, 2023
Browse files
Options
Browse Files
Download
Plain Diff
!95 新增SSO登录
Merge pull request !95 from puhui999/dev
parents
1e0f6134
841040b4
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
286 additions
and
24 deletions
+286
-24
.env.dev
+1
-1
src/api/login/index.ts
+51
-0
src/config/axios/service.ts
+2
-2
src/locales/zh-CN.ts
+7
-0
src/router/index.ts
+2
-2
src/router/modules/remaining.ts
+10
-0
src/types/auto-components.d.ts
+1
-6
src/views/Login/Login.vue
+11
-8
src/views/Login/components/LoginForm.vue
+8
-2
src/views/Login/components/LoginFormTitle.vue
+2
-1
src/views/Login/components/SSOLogin.vue
+187
-0
src/views/Login/components/index.ts
+2
-1
src/views/Login/components/useLogin.ts
+2
-1
No files found.
.env.dev
View file @
0f0ba8b8
...
@@ -16,7 +16,7 @@ VITE_API_BASEPATH=/dev-api
...
@@ -16,7 +16,7 @@ VITE_API_BASEPATH=/dev-api
VITE_API_URL=/admin-api
VITE_API_URL=/admin-api
# 打包路径
# 打包路径
VITE_BASE_PATH=/
dist-dev/
VITE_BASE_PATH=/
# 是否删除debugger
# 是否删除debugger
VITE_DROP_DEBUGGER=false
VITE_DROP_DEBUGGER=false
...
...
src/api/login/index.ts
View file @
0f0ba8b8
import
request
from
'@/config/axios'
import
request
from
'@/config/axios'
import
{
getRefreshToken
}
from
'@/utils/auth'
import
{
getRefreshToken
}
from
'@/utils/auth'
import
type
{
UserLoginVO
}
from
'./types'
import
type
{
UserLoginVO
}
from
'./types'
import
{
service
}
from
'@/config/axios/service'
export
interface
CodeImgResult
{
export
interface
CodeImgResult
{
captchaOnOff
:
boolean
captchaOnOff
:
boolean
img
:
string
img
:
string
uuid
:
string
uuid
:
string
}
}
export
interface
SmsCodeVO
{
export
interface
SmsCodeVO
{
mobile
:
string
mobile
:
string
scene
:
number
scene
:
number
}
}
export
interface
SmsLoginVO
{
export
interface
SmsLoginVO
{
mobile
:
string
mobile
:
string
code
:
string
code
:
string
...
@@ -71,3 +74,51 @@ export const getCode = (data) => {
...
@@ -71,3 +74,51 @@ export const getCode = (data) => {
export
const
reqCheck
=
(
data
)
=>
{
export
const
reqCheck
=
(
data
)
=>
{
return
request
.
postOriginal
({
url
:
'system/captcha/check'
,
data
})
return
request
.
postOriginal
({
url
:
'system/captcha/check'
,
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
})
}
export
function
authorize
(
responseType
:
string
,
clientId
:
string
,
redirectUri
:
string
,
state
:
string
,
autoApprove
:
boolean
,
checkedScopes
:
scopesType
,
uncheckedScopes
:
scopesType
)
{
// 构建 scopes
const
scopes
=
{}
for
(
const
scope
of
checkedScopes
)
{
scopes
[
scope
]
=
true
}
for
(
const
scope
of
uncheckedScopes
)
{
scopes
[
scope
]
=
false
}
// 发起请求
return
service
({
url
:
'/system/oauth2/authorize'
,
headers
:
{
'Content-type'
:
'application/x-www-form-urlencoded'
},
params
:
{
response_type
:
responseType
,
client_id
:
clientId
,
redirect_uri
:
redirectUri
,
state
:
state
,
auto_approve
:
autoApprove
,
scope
:
JSON
.
stringify
(
scopes
)
},
method
:
'post'
})
}
src/config/axios/service.ts
View file @
0f0ba8b8
import
axios
,
{
import
axios
,
{
AxiosError
,
AxiosInstance
,
AxiosInstance
,
AxiosRequestHeaders
,
AxiosRequestHeaders
,
AxiosResponse
,
AxiosResponse
,
AxiosError
,
InternalAxiosRequestConfig
InternalAxiosRequestConfig
}
from
'axios'
}
from
'axios'
...
@@ -230,7 +230,7 @@ const handleAuthorized = () => {
...
@@ -230,7 +230,7 @@ const handleAuthorized = () => {
wsCache
.
clear
()
wsCache
.
clear
()
removeToken
()
removeToken
()
isRelogin
.
show
=
false
isRelogin
.
show
=
false
window
.
location
.
href
=
import
.
meta
.
env
.
VITE_BASE_PATH
window
.
location
.
href
=
'/login?redirect=/sso?'
+
window
.
location
.
href
.
split
(
'?'
)[
1
]
})
})
}
}
return
Promise
.
reject
(
t
(
'sys.api.timeoutMessage'
))
return
Promise
.
reject
(
t
(
'sys.api.timeoutMessage'
))
...
...
src/locales/zh-CN.ts
View file @
0f0ba8b8
...
@@ -129,6 +129,12 @@ export default {
...
@@ -129,6 +129,12 @@ export default {
btnMobile
:
'手机登录'
,
btnMobile
:
'手机登录'
,
btnQRCode
:
'二维码登录'
,
btnQRCode
:
'二维码登录'
,
qrcode
:
'扫描二维码登录'
,
qrcode
:
'扫描二维码登录'
,
sso
:
{
user
:
{
read
:
'访问你的个人信息'
,
write
:
'修改你的个人信息'
}
},
btnRegister
:
'注册'
,
btnRegister
:
'注册'
,
SmsSendMsg
:
'验证码已发送'
SmsSendMsg
:
'验证码已发送'
},
},
...
@@ -352,6 +358,7 @@ export default {
...
@@ -352,6 +358,7 @@ export default {
login
:
{
login
:
{
backSignIn
:
'返回'
,
backSignIn
:
'返回'
,
signInFormTitle
:
'登录'
,
signInFormTitle
:
'登录'
,
ssoFormTitle
:
'三方授权'
,
mobileSignInFormTitle
:
'手机登录'
,
mobileSignInFormTitle
:
'手机登录'
,
qrSignInFormTitle
:
'二维码登录'
,
qrSignInFormTitle
:
'二维码登录'
,
signUpFormTitle
:
'注册'
,
signUpFormTitle
:
'注册'
,
...
...
src/router/index.ts
View file @
0f0ba8b8
import
type
{
App
}
from
'vue'
import
type
{
App
}
from
'vue'
import
type
{
RouteRecordRaw
}
from
'vue-router'
import
type
{
RouteRecordRaw
}
from
'vue-router'
import
{
createRouter
,
createWebH
ashH
istory
}
from
'vue-router'
import
{
createRouter
,
createWebHistory
}
from
'vue-router'
import
remainingRouter
from
'./modules/remaining'
import
remainingRouter
from
'./modules/remaining'
// 创建路由实例
// 创建路由实例
const
router
=
createRouter
({
const
router
=
createRouter
({
history
:
createWebH
ashH
istory
(),
// createWebHashHistory URL带#,createWebHistory URL不带#
history
:
createWebHistory
(),
// createWebHashHistory URL带#,createWebHistory URL不带#
strict
:
true
,
strict
:
true
,
routes
:
remainingRouter
as
RouteRecordRaw
[],
routes
:
remainingRouter
as
RouteRecordRaw
[],
scrollBehavior
:
()
=>
({
left
:
0
,
top
:
0
})
scrollBehavior
:
()
=>
({
left
:
0
,
top
:
0
})
...
...
src/router/modules/remaining.ts
View file @
0f0ba8b8
...
@@ -186,6 +186,16 @@ const remainingRouter: AppRouteRecordRaw[] = [
...
@@ -186,6 +186,16 @@ const remainingRouter: AppRouteRecordRaw[] = [
}
}
},
},
{
{
path
:
'/sso'
,
component
:
()
=>
import
(
'@/views/Login/Login.vue'
),
name
:
'SSOLogin'
,
meta
:
{
hidden
:
true
,
title
:
t
(
'router.login'
),
noTagsView
:
true
}
},
{
path
:
'/403'
,
path
:
'/403'
,
component
:
()
=>
import
(
'@/views/Error/403.vue'
),
component
:
()
=>
import
(
'@/views/Error/403.vue'
),
name
:
'NoAccess'
,
name
:
'NoAccess'
,
...
...
src/types/auto-components.d.ts
View file @
0f0ba8b8
...
@@ -21,15 +21,14 @@ declare module '@vue/runtime-core' {
...
@@ -21,15 +21,14 @@ declare module '@vue/runtime-core' {
Descriptions
:
typeof
import
(
'./../components/Descriptions/src/Descriptions.vue'
)[
'default'
]
Descriptions
:
typeof
import
(
'./../components/Descriptions/src/Descriptions.vue'
)[
'default'
]
Dialog
:
typeof
import
(
'./../components/Dialog/src/Dialog.vue'
)[
'default'
]
Dialog
:
typeof
import
(
'./../components/Dialog/src/Dialog.vue'
)[
'default'
]
DictTag
:
typeof
import
(
'./../components/DictTag/src/DictTag.vue'
)[
'default'
]
DictTag
:
typeof
import
(
'./../components/DictTag/src/DictTag.vue'
)[
'default'
]
DocAlert
:
typeof
import
(
'./../components/DocAlert/index.vue'
)[
'default'
]
Echart
:
typeof
import
(
'./../components/Echart/src/Echart.vue'
)[
'default'
]
Echart
:
typeof
import
(
'./../components/Echart/src/Echart.vue'
)[
'default'
]
Editor
:
typeof
import
(
'./../components/Editor/src/Editor.vue'
)[
'default'
]
Editor
:
typeof
import
(
'./../components/Editor/src/Editor.vue'
)[
'default'
]
ElAlert
:
typeof
import
(
'element-plus/es'
)[
'ElAlert'
]
ElBadge
:
typeof
import
(
'element-plus/es'
)[
'ElBadge'
]
ElBadge
:
typeof
import
(
'element-plus/es'
)[
'ElBadge'
]
ElButton
:
typeof
import
(
'element-plus/es'
)[
'ElButton'
]
ElButton
:
typeof
import
(
'element-plus/es'
)[
'ElButton'
]
ElButtonGroup
:
typeof
import
(
'element-plus/es'
)[
'ElButtonGroup'
]
ElButtonGroup
:
typeof
import
(
'element-plus/es'
)[
'ElButtonGroup'
]
ElCard
:
typeof
import
(
'element-plus/es'
)[
'ElCard'
]
ElCard
:
typeof
import
(
'element-plus/es'
)[
'ElCard'
]
ElCheckbox
:
typeof
import
(
'element-plus/es'
)[
'ElCheckbox'
]
ElCheckbox
:
typeof
import
(
'element-plus/es'
)[
'ElCheckbox'
]
ElCheckboxGroup
:
typeof
import
(
'element-plus/es'
)[
'ElCheckboxGroup'
]
ElCol
:
typeof
import
(
'element-plus/es'
)[
'ElCol'
]
ElCol
:
typeof
import
(
'element-plus/es'
)[
'ElCol'
]
ElCollapse
:
typeof
import
(
'element-plus/es'
)[
'ElCollapse'
]
ElCollapse
:
typeof
import
(
'element-plus/es'
)[
'ElCollapse'
]
ElCollapseItem
:
typeof
import
(
'element-plus/es'
)[
'ElCollapseItem'
]
ElCollapseItem
:
typeof
import
(
'element-plus/es'
)[
'ElCollapseItem'
]
...
@@ -74,12 +73,8 @@ declare module '@vue/runtime-core' {
...
@@ -74,12 +73,8 @@ declare module '@vue/runtime-core' {
ElTableColumn
:
typeof
import
(
'element-plus/es'
)[
'ElTableColumn'
]
ElTableColumn
:
typeof
import
(
'element-plus/es'
)[
'ElTableColumn'
]
ElTabPane
:
typeof
import
(
'element-plus/es'
)[
'ElTabPane'
]
ElTabPane
:
typeof
import
(
'element-plus/es'
)[
'ElTabPane'
]
ElTabs
:
typeof
import
(
'element-plus/es'
)[
'ElTabs'
]
ElTabs
:
typeof
import
(
'element-plus/es'
)[
'ElTabs'
]
ElTag
:
typeof
import
(
'element-plus/es'
)[
'ElTag'
]
ElTimeline
:
typeof
import
(
'element-plus/es'
)[
'ElTimeline'
]
ElTimelineItem
:
typeof
import
(
'element-plus/es'
)[
'ElTimelineItem'
]
ElTooltip
:
typeof
import
(
'element-plus/es'
)[
'ElTooltip'
]
ElTooltip
:
typeof
import
(
'element-plus/es'
)[
'ElTooltip'
]
ElTree
:
typeof
import
(
'element-plus/es'
)[
'ElTree'
]
ElTree
:
typeof
import
(
'element-plus/es'
)[
'ElTree'
]
ElTreeSelect
:
typeof
import
(
'element-plus/es'
)[
'ElTreeSelect'
]
ElUpload
:
typeof
import
(
'element-plus/es'
)[
'ElUpload'
]
ElUpload
:
typeof
import
(
'element-plus/es'
)[
'ElUpload'
]
Error
:
typeof
import
(
'./../components/Error/src/Error.vue'
)[
'default'
]
Error
:
typeof
import
(
'./../components/Error/src/Error.vue'
)[
'default'
]
FlowCondition
:
typeof
import
(
'./../components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue'
)[
'default'
]
FlowCondition
:
typeof
import
(
'./../components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue'
)[
'default'
]
...
...
src/views/Login/Login.vue
View file @
0f0ba8b8
...
@@ -9,19 +9,19 @@
...
@@ -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 @@
...
@@ -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,20 +52,23 @@
...
@@ -52,20 +52,23 @@
<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
)"
/>
<!-- 三方登录 -->
<SSOLoginVue
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'
import
{
useAppStore
}
from
'@/store/modules/app'
import
{
useAppStore
}
from
'@/store/modules/app'
import
{
ThemeSwitch
}
from
'@/layout/components/ThemeSwitch'
import
{
ThemeSwitch
}
from
'@/layout/components/ThemeSwitch'
import
{
LocaleDropdown
}
from
'@/layout/components/LocaleDropdown'
import
{
LocaleDropdown
}
from
'@/layout/components/LocaleDropdown'
import
{
LoginForm
,
MobileForm
,
RegisterForm
,
QrCodeForm
}
from
'./components'
import
{
LoginForm
,
MobileForm
,
QrCodeForm
,
RegisterForm
,
SSOLoginVue
}
from
'./components'
const
{
t
}
=
useI18n
()
const
{
t
}
=
useI18n
()
const
appStore
=
useAppStore
()
const
appStore
=
useAppStore
()
...
...
src/views/Login/components/LoginForm.vue
View file @
0f0ba8b8
...
@@ -137,7 +137,7 @@ import { useIcon } from '@/hooks/web/useIcon'
...
@@ -137,7 +137,7 @@ import { useIcon } from '@/hooks/web/useIcon'
import
*
as
authUtil
from
'@/utils/auth'
import
*
as
authUtil
from
'@/utils/auth'
import
{
usePermissionStore
}
from
'@/store/modules/permission'
import
{
usePermissionStore
}
from
'@/store/modules/permission'
import
*
as
LoginApi
from
'@/api/login'
import
*
as
LoginApi
from
'@/api/login'
import
{
LoginStateEnum
,
use
LoginState
,
useFormValid
}
from
'./useLogin'
import
{
LoginStateEnum
,
use
FormValid
,
useLoginState
}
from
'./useLogin'
const
{
t
}
=
useI18n
()
const
{
t
}
=
useI18n
()
const
message
=
useMessage
()
const
message
=
useMessage
()
...
@@ -240,7 +240,12 @@ const handleLogin = async (params) => {
...
@@ -240,7 +240,12 @@ const handleLogin = async (params) => {
if
(
!
redirect
.
value
)
{
if
(
!
redirect
.
value
)
{
redirect
.
value
=
'/'
redirect
.
value
=
'/'
}
}
push
({
path
:
redirect
.
value
||
permissionStore
.
addRouters
[
0
].
path
})
// 判断是否为SSO登录
if
(
redirect
.
value
.
indexOf
(
'sso'
)
!==
-
1
)
{
window
.
location
.
href
=
window
.
location
.
href
.
replace
(
'/login?redirect='
,
''
)
}
else
{
push
({
path
:
redirect
.
value
||
permissionStore
.
addRouters
[
0
].
path
})
}
}
catch
{
}
catch
{
loginLoading
.
value
=
false
loginLoading
.
value
=
false
}
finally
{
}
finally
{
...
@@ -291,6 +296,7 @@ onMounted(() => {
...
@@ -291,6 +296,7 @@ onMounted(() => {
color
:
var
(
--el-color-primary
)
!important
;
color
:
var
(
--el-color-primary
)
!important
;
}
}
}
}
.login-code
{
.login-code
{
width
:
100%
;
width
:
100%
;
height
:
38px
;
height
:
38px
;
...
...
src/views/Login/components/LoginFormTitle.vue
View file @
0f0ba8b8
...
@@ -16,7 +16,8 @@ const getFormTitle = computed(() => {
...
@@ -16,7 +16,8 @@ const getFormTitle = computed(() => {
[
LoginStateEnum
.
LOGIN
]:
t
(
'sys.login.signInFormTitle'
),
[
LoginStateEnum
.
LOGIN
]:
t
(
'sys.login.signInFormTitle'
),
[
LoginStateEnum
.
REGISTER
]:
t
(
'sys.login.signUpFormTitle'
),
[
LoginStateEnum
.
REGISTER
]:
t
(
'sys.login.signUpFormTitle'
),
[
LoginStateEnum
.
MOBILE
]:
t
(
'sys.login.mobileSignInFormTitle'
),
[
LoginStateEnum
.
MOBILE
]:
t
(
'sys.login.mobileSignInFormTitle'
),
[
LoginStateEnum
.
QR_CODE
]:
t
(
'sys.login.qrSignInFormTitle'
)
[
LoginStateEnum
.
QR_CODE
]:
t
(
'sys.login.qrSignInFormTitle'
),
[
LoginStateEnum
.
SSO
]:
t
(
'sys.login.ssoFormTitle'
)
}
}
return
titleObj
[
unref
(
getLoginState
)]
return
titleObj
[
unref
(
getLoginState
)]
})
})
...
...
src/views/Login/components/SSOLogin.vue
0 → 100644
View file @
0f0ba8b8
<
template
>
<!-- 表单 -->
<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>
<div>
<el-form
ref=
"ssoForm"
:model=
"loginForm"
class=
"login-form"
>
<!-- 授权范围的选择 -->
此第三方应用请求获得以下权限:
<el-form-item
prop=
"scopes"
>
<el-checkbox-group
v-model=
"loginForm.scopes"
>
<el-checkbox
v-for=
"scope in params.scopes"
:key=
"scope"
:label=
"scope"
style=
"display: block; margin-bottom: -10px"
>
{{
formatScope
(
scope
)
}}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<!-- 下方的登录按钮 -->
<el-form-item
style=
"width: 100%"
>
<el-button
:loading=
"loading"
size=
"small"
style=
"width: 60%"
type=
"primary"
@
click
.
prevent=
"handleAuthorize(true)"
>
<span
v-if=
"!loading"
>
同意授权
</span>
<span
v-else
>
授 权 中...
</span>
</el-button>
<el-button
size=
"small"
style=
"width: 36%"
@
click
.
prevent=
"handleAuthorize(false)"
>
拒绝
</el-button>
</el-form-item>
</el-form>
</div>
</div>
</
template
>
<
script
lang=
"ts"
name=
"SSOLogin"
setup
>
// 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
const
{
getLoginState
,
setLoginState
}
=
useLoginState
()
const
getShow
=
computed
(()
=>
unref
(
getLoginState
)
===
LoginStateEnum
.
SSO
)
const
loginForm
=
reactive
<
{
scopes
:
scopesType
}
>
({
scopes
:
[]
// 已选中的 scope 数组
})
const
params
=
reactive
<
paramsType
>
({
// URL 上的 client_id、scope 等参数
responseType
:
''
,
clientId
:
''
,
redirectUri
:
''
,
state
:
''
,
scopes
:
[]
// 优先从 query 参数获取;如果未传递,从后端获取
})
// 表单Ref
const
client
=
ref
({
// 客户端信息
name
:
''
,
logo
:
''
})
const
loading
=
ref
(
false
)
const
handleAuthorize
=
(
approved
)
=>
{
ssoForm
.
value
.
validate
((
valid
)
=>
{
if
(
!
valid
)
{
return
}
loading
.
value
=
true
// 计算 checkedScopes + uncheckedScopes
let
checkedScopes
let
uncheckedScopes
if
(
approved
)
{
// 同意授权,按照用户的选择
checkedScopes
=
loginForm
.
scopes
uncheckedScopes
=
params
.
scopes
.
filter
((
item
)
=>
checkedScopes
.
indexOf
(
item
)
===
-
1
)
}
else
{
// 拒绝,则都是取消
checkedScopes
=
[]
uncheckedScopes
=
params
.
scopes
}
// 提交授权的请求
doAuthorize
(
false
,
checkedScopes
,
uncheckedScopes
)
.
then
((
res
)
=>
{
const
href
=
res
.
data
if
(
!
href
)
{
return
}
location
.
href
=
href
})
.
finally
(()
=>
{
loading
.
value
=
false
})
})
}
const
doAuthorize
=
(
autoApprove
,
checkedScopes
,
uncheckedScopes
)
=>
{
return
authorize
(
params
.
responseType
,
params
.
clientId
,
params
.
redirectUri
,
params
.
state
,
autoApprove
,
checkedScopes
,
uncheckedScopes
)
}
const
formatScope
=
(
scope
)
=>
{
// 格式化 scope 授权范围,方便用户理解。
// 这里仅仅是一个 demo,可以考虑录入到字典数据中,例如说字典类型 "system_oauth2_scope",它的每个 scope 都是一条字典数据。
// TODO 这个之做了中文部分
return
t
(
`login.sso.
${
scope
}
`
)
}
const
route
=
useRoute
()
const
init
=
()
=>
{
// 防止在没有登录的情况下循环弹窗
if
(
typeof
route
.
query
.
client_id
===
'undefined'
)
return
// 解析参数
// 例如说【自动授权不通过】:client_id=default&redirect_uri=https%3A%2F%2Fwww.iocoder.cn&response_type=code&scope=user.read%20user.write
// 例如说【自动授权通过】:client_id=default&redirect_uri=https%3A%2F%2Fwww.iocoder.cn&response_type=code&scope=user.read
params
.
responseType
=
route
.
query
.
response_type
as
string
params
.
clientId
=
route
.
query
.
client_id
as
string
params
.
redirectUri
=
route
.
query
.
redirect_uri
as
string
params
.
state
=
route
.
query
.
state
as
string
if
(
route
.
query
.
scope
)
{
params
.
scopes
=
(
route
.
query
.
scope
as
string
).
split
(
' '
)
}
// 如果有 scope 参数,先执行一次自动授权,看看是否之前都授权过了。
if
(
params
.
scopes
.
length
>
0
)
{
doAuthorize
(
true
,
params
.
scopes
,
[]).
then
((
res
)
=>
{
if
(
!
res
)
{
console
.
log
(
'自动授权未通过!'
)
return
}
location
.
href
=
res
.
data
})
}
// 获取授权页的基本信息
getAuthorize
(
params
.
clientId
).
then
((
res
)
=>
{
client
.
value
=
res
.
client
// 解析 scope
let
scopes
// 1.1 如果 params.scope 非空,则过滤下返回的 scopes
if
(
params
.
scopes
.
length
>
0
)
{
scopes
=
[]
for
(
const
scope
of
res
.
scopes
)
{
if
(
params
.
scopes
.
indexOf
(
scope
.
key
)
>=
0
)
{
scopes
.
push
(
scope
)
}
}
// 1.2 如果 params.scope 为空,则使用返回的 scopes 设置它
}
else
{
scopes
=
res
.
scopes
for
(
const
scope
of
scopes
)
{
params
.
scopes
.
push
(
scope
.
key
)
}
}
// 生成已选中的 checkedScopes
for
(
const
scope
of
scopes
)
{
if
(
scope
.
value
)
{
loginForm
.
scopes
.
push
(
scope
.
key
)
}
}
})
}
// =======SSO======
const
{
currentRoute
}
=
useRouter
()
// 监听当前路由
watch
(
()
=>
currentRoute
.
value
,
(
route
:
RouteLocationNormalizedLoaded
)
=>
{
if
(
route
.
name
===
'SSOLogin'
)
{
setLoginState
(
LoginStateEnum
.
SSO
)
init
()
}
},
{
immediate
:
true
}
)
init
()
</
script
>
src/views/Login/components/index.ts
View file @
0f0ba8b8
...
@@ -3,5 +3,6 @@ import MobileForm from './MobileForm.vue'
...
@@ -3,5 +3,6 @@ import MobileForm from './MobileForm.vue'
import
LoginFormTitle
from
'./LoginFormTitle.vue'
import
LoginFormTitle
from
'./LoginFormTitle.vue'
import
RegisterForm
from
'./RegisterForm.vue'
import
RegisterForm
from
'./RegisterForm.vue'
import
QrCodeForm
from
'./QrCodeForm.vue'
import
QrCodeForm
from
'./QrCodeForm.vue'
import
SSOLoginVue
from
'./SSOLogin.vue'
export
{
LoginForm
,
MobileForm
,
LoginFormTitle
,
RegisterForm
,
QrCodeForm
}
export
{
LoginForm
,
MobileForm
,
LoginFormTitle
,
RegisterForm
,
QrCodeForm
,
SSOLoginVue
}
src/views/Login/components/useLogin.ts
View file @
0f0ba8b8
...
@@ -5,7 +5,8 @@ export enum LoginStateEnum {
...
@@ -5,7 +5,8 @@ export enum LoginStateEnum {
REGISTER
,
REGISTER
,
RESET_PASSWORD
,
RESET_PASSWORD
,
MOBILE
,
MOBILE
,
QR_CODE
QR_CODE
,
SSO
}
}
const
currentState
=
ref
(
LoginStateEnum
.
LOGIN
)
const
currentState
=
ref
(
LoginStateEnum
.
LOGIN
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment