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
Commit
2d019f1b
authored
Apr 07, 2023
by
puhui999
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
增加SSO单点登录
parent
f16ea74a
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
276 additions
and
22 deletions
+276
-22
src/api/login/index.ts
+44
-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
-13
src/views/Login/Login.vue
+19
-1
src/views/Login/components/LoginForm.vue
+8
-1
src/views/Login/components/LoginFormTitle.vue
+2
-1
src/views/Login/components/SSOLogin.vue
+177
-0
src/views/Login/components/index.ts
+2
-1
src/views/Login/components/useLogin.ts
+2
-1
No files found.
src/api/login/index.ts
View file @
2d019f1b
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,44 @@ export const getCodeApi = (data) => {
...
@@ -71,3 +74,44 @@ export const getCodeApi = (data) => {
export
const
reqCheckApi
=
(
data
)
=>
{
export
const
reqCheckApi
=
(
data
)
=>
{
return
request
.
postOriginal
({
url
:
'system/captcha/check'
,
data
})
return
request
.
postOriginal
({
url
:
'system/captcha/check'
,
data
})
}
}
// ========== OAUTH 2.0 相关 ==========
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
:
any
,
uncheckedScopes
:
any
)
{
// 构建 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 @
2d019f1b
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
=
'/
'
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 @
2d019f1b
...
@@ -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
:
'验证码已发送'
},
},
...
@@ -353,6 +359,7 @@ export default {
...
@@ -353,6 +359,7 @@ export default {
login
:
{
login
:
{
backSignIn
:
'返回'
,
backSignIn
:
'返回'
,
signInFormTitle
:
'登录'
,
signInFormTitle
:
'登录'
,
ssoFormTitle
:
'三方授权'
,
mobileSignInFormTitle
:
'手机登录'
,
mobileSignInFormTitle
:
'手机登录'
,
qrSignInFormTitle
:
'二维码登录'
,
qrSignInFormTitle
:
'二维码登录'
,
signUpFormTitle
:
'注册'
,
signUpFormTitle
:
'注册'
,
...
...
src/router/index.ts
View file @
2d019f1b
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 @
2d019f1b
...
@@ -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 @
2d019f1b
...
@@ -23,13 +23,12 @@ declare module '@vue/runtime-core' {
...
@@ -23,13 +23,12 @@ declare module '@vue/runtime-core' {
DictTag
:
typeof
import
(
'./../components/DictTag/src/DictTag.vue'
)[
'default'
]
DictTag
:
typeof
import
(
'./../components/DictTag/src/DictTag.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'
]
ElAutoResizer
:
typeof
import
(
'element-plus/es'
)[
'ElAutoResizer'
]
ElAvatar
:
typeof
import
(
'element-plus/es'
)[
'ElAvatar'
]
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'
]
...
@@ -54,34 +53,23 @@ declare module '@vue/runtime-core' {
...
@@ -54,34 +53,23 @@ declare module '@vue/runtime-core' {
ElForm
:
typeof
import
(
'element-plus/es'
)[
'ElForm'
]
ElForm
:
typeof
import
(
'element-plus/es'
)[
'ElForm'
]
ElFormItem
:
typeof
import
(
'element-plus/es'
)[
'ElFormItem'
]
ElFormItem
:
typeof
import
(
'element-plus/es'
)[
'ElFormItem'
]
ElIcon
:
typeof
import
(
'element-plus/es'
)[
'ElIcon'
]
ElIcon
:
typeof
import
(
'element-plus/es'
)[
'ElIcon'
]
ElImage
:
typeof
import
(
'element-plus/es'
)[
'ElImage'
]
ElImageViewer
:
typeof
import
(
'element-plus/es'
)[
'ElImageViewer'
]
ElImageViewer
:
typeof
import
(
'element-plus/es'
)[
'ElImageViewer'
]
ElInput
:
typeof
import
(
'element-plus/es'
)[
'ElInput'
]
ElInput
:
typeof
import
(
'element-plus/es'
)[
'ElInput'
]
ElInputNumber
:
typeof
import
(
'element-plus/es'
)[
'ElInputNumber'
]
ElLink
:
typeof
import
(
'element-plus/es'
)[
'ElLink'
]
ElLink
:
typeof
import
(
'element-plus/es'
)[
'ElLink'
]
ElOption
:
typeof
import
(
'element-plus/es'
)[
'ElOption'
]
ElOption
:
typeof
import
(
'element-plus/es'
)[
'ElOption'
]
ElPagination
:
typeof
import
(
'element-plus/es'
)[
'ElPagination'
]
ElPagination
:
typeof
import
(
'element-plus/es'
)[
'ElPagination'
]
ElPopover
:
typeof
import
(
'element-plus/es'
)[
'ElPopover'
]
ElPopover
:
typeof
import
(
'element-plus/es'
)[
'ElPopover'
]
ElRadio
:
typeof
import
(
'element-plus/es'
)[
'ElRadio'
]
ElRadioButton
:
typeof
import
(
'element-plus/es'
)[
'ElRadioButton'
]
ElRadioGroup
:
typeof
import
(
'element-plus/es'
)[
'ElRadioGroup'
]
ElRow
:
typeof
import
(
'element-plus/es'
)[
'ElRow'
]
ElRow
:
typeof
import
(
'element-plus/es'
)[
'ElRow'
]
ElScrollbar
:
typeof
import
(
'element-plus/es'
)[
'ElScrollbar'
]
ElScrollbar
:
typeof
import
(
'element-plus/es'
)[
'ElScrollbar'
]
ElSelect
:
typeof
import
(
'element-plus/es'
)[
'ElSelect'
]
ElSelect
:
typeof
import
(
'element-plus/es'
)[
'ElSelect'
]
ElSkeleton
:
typeof
import
(
'element-plus/es'
)[
'ElSkeleton'
]
ElSkeleton
:
typeof
import
(
'element-plus/es'
)[
'ElSkeleton'
]
ElSpace
:
typeof
import
(
'element-plus/es'
)[
'ElSpace'
]
ElSwitch
:
typeof
import
(
'element-plus/es'
)[
'ElSwitch'
]
ElSwitch
:
typeof
import
(
'element-plus/es'
)[
'ElSwitch'
]
ElTable
:
typeof
import
(
'element-plus/es'
)[
'ElTable'
]
ElTable
:
typeof
import
(
'element-plus/es'
)[
'ElTable'
]
ElTableColumn
:
typeof
import
(
'element-plus/es'
)[
'ElTableColumn'
]
ElTableColumn
:
typeof
import
(
'element-plus/es'
)[
'ElTableColumn'
]
ElTableV2
:
typeof
import
(
'element-plus/es'
)[
'ElTableV2'
]
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 @
2d019f1b
...
@@ -52,6 +52,11 @@
...
@@ -52,6 +52,11 @@
<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
v-if=
"isSSO"
class=
"p-20px h-auto m-auto
<xl:
(
rounded-3xl
light:bg-white
)"
/>
</div>
</div>
</Transition>
</Transition>
</div>
</div>
...
@@ -65,12 +70,25 @@ import { useDesign } from '@/hooks/web/useDesign'
...
@@ -65,12 +70,25 @@ 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'
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
>
...
...
src/views/Login/components/LoginForm.vue
View file @
2d019f1b
...
@@ -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
=
'/'
}
}
// 判断是否为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
})
push
({
path
:
redirect
.
value
||
permissionStore
.
addRouters
[
0
].
path
})
}
}
catch
{
}
catch
{
loginLoading
.
value
=
false
loginLoading
.
value
=
false
}
finally
{
}
finally
{
...
@@ -274,6 +279,7 @@ const doSocialLogin = async (type: number) => {
...
@@ -274,6 +279,7 @@ 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
},
},
{
{
...
@@ -291,6 +297,7 @@ onMounted(() => {
...
@@ -291,6 +297,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 @
2d019f1b
...
@@ -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 @
2d019f1b
<
template
>
<!-- 表单 -->
<div
class=
"form-cont"
>
<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"
:label=
"scope"
:key=
"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"
type=
"primary"
style=
"width: 60%"
@
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
{
authorize
,
getAuthorize
}
from
'@/api/login'
const
{
t
}
=
useI18n
()
const
ssoForm
=
ref
()
// 表单Ref
type
scopesType
=
string
[]
interface
paramsType
{
responseType
:
string
clientId
:
string
redirectUri
:
string
state
:
string
scopes
:
scopesType
}
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 都是一条字典数据。
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
)
=>
{
console
.
log
(
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
)
}
}
})
}
init
()
</
script
>
src/views/Login/components/index.ts
View file @
2d019f1b
...
@@ -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 @
2d019f1b
...
@@ -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