Browse Source

Merge branch 'zhangtao' of gitadmin/tuoheng_pilot_web into develop

tags/v1.0.0
zhangtao 1 year ago
parent
commit
9c34623d95
14 changed files with 278 additions and 133 deletions
  1. +2
    -1
      .env
  2. +7
    -2
      .env.development
  3. +6
    -1
      .env.localhost
  4. +6
    -1
      .env.production
  5. +7
    -2
      .env.test
  6. +1
    -0
      package.json
  7. +1
    -1
      src/api/login/index.js
  8. +26
    -16
      src/layout/components/Header/index.vue
  9. +4
    -4
      src/layout/index.vue
  10. +22
    -27
      src/router/guard/permission-guard.js
  11. +34
    -49
      src/store/modules/user.js
  12. +22
    -26
      src/utils/http/interceptors.js
  13. +91
    -0
      src/utils/oidc/index.js
  14. +49
    -3
      src/views/login/index.vue

+ 2
- 1
.env View File

# 端口号 # 端口号
VITE_PORT = 3050 VITE_PORT = 3050


VITE_SERVER = "/pilot/admin"


VITE_SERVER = "/pilot/admin"
VITE_PLATFORM = "tuoheng-dsp-web"

+ 7
- 2
.env.development View File

VITE_APP_USE_MOCK = false VITE_APP_USE_MOCK = false


# proxy # proxy
VITE_PROXY = [["/api","http://192.168.11.11:9055"]]
VITE_PROXY = [["","http://192.168.11.11:7011"]]


# base api # base api
VITE_APP_GLOB_BASE_API = '/api'
VITE_APP_GLOB_BASE_API = ''

VITE_AUTHORITY = 'http://oidc.dev.t-aaron.com'
VITE_CLIENT_ID = 'tuoheng-pilot-admin'
VITE_CLIENT_SECRET = 'WB0CZ1c6bZLiYP6jLtDFsA=='
VITE_REDIRECT_URI = 'http://192.168.11.11:7011/login'

+ 6
- 1
.env.localhost View File

VITE_APP_GLOB_BASE_API = '/api-local' VITE_APP_GLOB_BASE_API = '/api-local'


# mock base api # mock base api
VITE_APP_GLOB_BASE_API_MOCK = '/api-mock'
VITE_APP_GLOB_BASE_API_MOCK = '/api-mock'

VITE_AUTHORITY = 'http://oidc.dev.t-aaron.com'
VITE_CLIENT_ID = 'tuoheng-pilot-admin'
VITE_CLIENT_SECRET = 'WB0CZ1c6bZLiYP6jLtDFsA=='
VITE_REDIRECT_URI = 'http://192.168.12.8:3050/login'

+ 6
- 1
.env.production View File

VITE_PROXY = [["/api","http://127.0.0.1:8002/api"]] VITE_PROXY = [["/api","http://127.0.0.1:8002/api"]]


# base api # base api
VITE_APP_GLOB_BASE_API = '/api'
VITE_APP_GLOB_BASE_API = '/api'

VITE_AUTHORITY = 'http://oidc.dev.t-aaron.com'
VITE_CLIENT_ID = 'tuoheng-pilot-admin'
VITE_CLIENT_SECRET = 'WB0CZ1c6bZLiYP6jLtDFsA=='
VITE_REDIRECT_URI = 'http://192.168.11.11:7011/home'

+ 7
- 2
.env.test View File

VITE_APP_USE_MOCK = false VITE_APP_USE_MOCK = false


# proxy # proxy
VITE_PROXY = [["","http://192.168.11.11:9055"]]
VITE_PROXY = [["","http://192.168.11.241:9089"]]


# base api # base api
VITE_APP_GLOB_BASE_API = ''
VITE_APP_GLOB_BASE_API = ''

VITE_AUTHORITY = 'http://oidc.test.t-aaron.com'
VITE_CLIENT_ID = 'tuoheng-pilot-admin'
VITE_CLIENT_SECRET = 'WB0CZ1c6bZLiYP6jLtDFsA=='
VITE_REDIRECT_URI = 'http://192.168.11.241:9089/home'

+ 1
- 0
package.json View File

"chart-all": "^1.0.2", "chart-all": "^1.0.2",
"dayjs": "^1.11.2", "dayjs": "^1.11.2",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"oidc-client": "^1.11.5",
"pinia": "^2.0.13", "pinia": "^2.0.13",
"pinia-plugin-persist": "^1.0.0", "pinia-plugin-persist": "^1.0.0",
"tinymce": "^5.10.2", "tinymce": "^5.10.2",

+ 1
- 1
src/api/login/index.js View File

*/ */
export function getUser() { export function getUser() {
return request({ return request({
url: '/index/getUserInfo',
url: '/user/getUserInfo',
method: 'get' method: 'get'
}) })
} }

+ 26
- 16
src/layout/components/Header/index.vue View File

<n-image height="18" src="/logo.png" preview-disabled /> <n-image height="18" src="/logo.png" preview-disabled />
</div> </div>


<n-dropdown trigger="hover" :options="options" @select="handleSelect">
<n-dropdown v-if="getUserInfo.hasLogin" trigger="hover" :options="options" @select="handleSelect">
<div class="user_msg"> <div class="user_msg">
<n-image
<!-- <n-image
class="user_avatar" class="user_avatar"
:src="userInfo.avatar"
:src="getUserInfo.avatar"
preview-disabled preview-disabled
/>
<span class="user_name">{{ userInfo.realname }}</span>
/> -->
<span class="user_name">{{ getUserInfo.realname }}</span>
</div> </div>
</n-dropdown> </n-dropdown>
<div v-if="!getUserInfo.hasLogin" class="header__login" @click="handleLogin">登录</div>
</n-layout-header> </n-layout-header>
</template> </template>


import { renderIcon } from '@/utils' import { renderIcon } from '@/utils'
import { useUserStore } from '@/store/modules/user.js' import { useUserStore } from '@/store/modules/user.js'
import { useSettingStore } from '@/store/modules/setting.js' import { useSettingStore } from '@/store/modules/setting.js'
import { signinRedirect, signoutRedirect } from '@/utils/oidc/index.js'
export default defineComponent({ export default defineComponent({
name: 'LayoutHeader', name: 'LayoutHeader',
setup() { setup() {
const settingStore = useSettingStore() const settingStore = useSettingStore()
const data = reactive({ const data = reactive({
options: [ options: [
{
label: '修改密码',
key: 'edit',
icon: renderIcon(EditOutlined)
},
// {
// label: '修改密码',
// key: 'edit',
// icon: renderIcon(EditOutlined)
// },
{ {
label: '退出登录', label: '退出登录',
key: 'out', key: 'out',
async function handleSelect(key) { async function handleSelect(key) {
switch (key) { switch (key) {
case 'out': case 'out':
await logOut()
await signoutRedirect()
} }
} }


async function logOut() {
const res = await userStore.userLogout()
if (res.code === 0) {
router.replace('/login')
const getUserInfo = computed(() => {
return {
hasLogin: userStore.hasLogin,
// avatar: userStore.avatar,
username: userStore.username,
realname: userStore.realname
} }
})

const handleLogin = () => {
signinRedirect()
} }


return { return {
...toRefs(data), ...toRefs(data),
getLogoWidth, getLogoWidth,
handleSelect
getUserInfo,
handleSelect,
handleLogin
} }
} }
}) })

+ 4
- 4
src/layout/index.vue View File

const menuMode = computed(() => settingStore.getMenuMode) const menuMode = computed(() => settingStore.getMenuMode)
const tagsMenuSetting = computed(() => settingStore.getTagsMenuSetting) const tagsMenuSetting = computed(() => settingStore.getTagsMenuSetting)
const useUser = useUserStore() const useUser = useUserStore()
function getUserNow() {
useUser.getUserInfo()
}
getUserNow()
// function getUserNow() {
// useUser.getUserInfo()
// }
// getUserNow()


</script> </script>



+ 22
- 27
src/router/guard/permission-guard.js View File

import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
import { usePermissionStore } from '@/store/modules/permission' import { usePermissionStore } from '@/store/modules/permission'
import { NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '@/router/routes' import { NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '@/router/routes'
import { getToken } from '@/utils/token'
import { getUserInfo, signinRedirect, signoutRedirect } from '@/utils/oidc/index.js'


const WHITE_LIST = ['/login', '/redirect']
export function createPermissionGuard(router) { export function createPermissionGuard(router) {
const userStore = useUserStore() const userStore = useUserStore()
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore()
router.beforeEach(async(to, from, next) => { router.beforeEach(async(to, from, next) => {
// const token = getToken()
const token = true
if (token) {
if (to.path === '/login') {
next({ path: '/' })
const oidcUser = await getUserInfo()
if (oidcUser) {
const hasRoutes = !!permissionStore.permissionRoutes.length
if (hasRoutes) {
next()
} else { } else {
const hasRoutes = !!permissionStore.permissionRoutes.length
if (hasRoutes) {
next()
} else {
try {
// await userStore.getUserInfo()
const routes = await permissionStore.generateRoutesMock(['admin'])
routes.forEach((item) => {
router.addRoute(item)
})
router.addRoute(NOT_FOUND_ROUTE)
router.addRoute(REDIRECT_ROUTE)
next({ ...to, replace: true })
} catch (error) {
// removeToken()
// $message.error(error)
next({ path: '/login', query: { ...to.query, redirect: to.path }})
}
try {
// const { role } = oidcUser.profile
// const roles = role.includes('admin') ? 'admin' : 'flyer'
const userRole = await userStore.getUserInfos() || 1
const roleList = { 1: 'admin', 2: 'flyer' }
const routes = await permissionStore.generateRoutesMock([roleList[userRole]])
routes.forEach((item) => {
router.addRoute(item)
})
router.addRoute(NOT_FOUND_ROUTE)
router.addRoute(REDIRECT_ROUTE)
next({ ...to, replace: true })
} catch (error) {
signinRedirect()
} }
} }
} else { } else {
if (WHITE_LIST.includes(to.path)) {
if (to.path === '/login') {
next() next()
} else { } else {
next({ path: '/login', query: { ...to.query, redirect: to.path }})
signinRedirect()
} }
} }
}) })

+ 34
- 49
src/store/modules/user.js View File

import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { userLogin, loginOut } from '@/api/login'
import { getUser } from '@/api/login/index.js' import { getUser } from '@/api/login/index.js'
import { setToken, removeToken } from '@/utils/token'

import { useTagsMenuStore } from './tagsMenu.js'
import { usePermissionStore } from './permission.js'


export const useUserStore = defineStore('user', { export const useUserStore = defineStore('user', {
persist: true,
persist: {
enabled: true
},
state() { state() {
return { return {
userInfo: {}
userInfo: {
hasLogin: false
}
} }
}, },
getters: { getters: {
userInfoMsg() {
return this.userInfo
// userId() {
// return this.userInfo?.id
// },
userName() {
return this.userInfo?.userName
},
realname() {
return this.userInfo?.realname
},
avatar() {
return this.userInfo?.avatar
},
role() {
return this.userInfo?.role || []
},
authority() {
return this.userInfo.authority
},
hasLogin() {
return this.userInfo.hasLogin
} }
}, },
actions: { actions: {
/* 登录 */
async getLoginToken(form) {
try {
const res = await userLogin(form)
if (res.code === 0) {
/* 设置token */
setToken(res.data.access_token)
this.getUserInfo()
return Promise.resolve(res)
} else {
return Promise.reject(res)
}
} catch (error) {
return Promise.reject(error)
}
},
/* 获取用户信息 */
async getUserInfo() {
const res = await getUser()
if (res.code === 0) {
this.setUserInfo(res.data)
}
},
async userLogout() {
async getUserInfos() {
try { try {
const res = await loginOut()
const res = await getUser()
if (res.code === 0) { if (res.code === 0) {
removeToken()
this.reset()
return Promise.resolve(res)
this.setUserInfo({ hasLogin: true, ...res.data })
return Promise.resolve(res.data.type)
} else { } else {
return Promise.reject(res)
this.setUserInfo()
} }
} catch (error) { } catch (error) {
return Promise.reject(error)
console.error(error)
this.setUserInfo()
} }
}, },

reset() {
this.$reset()
const tagsMenuStore = useTagsMenuStore()
const permissionStore = usePermissionStore()
tagsMenuStore.$reset()
permissionStore.$reset()
},

setUserInfo(userInfo = {}) {
setUserInfo(userInfo = { hasLogin: false }) {
this.userInfo = { ...this.userInfo, ...userInfo } this.userInfo = { ...this.userInfo, ...userInfo }
} }
} }

+ 22
- 26
src/utils/http/interceptors.js View File

import { router } from '@/router'
import { getToken, removeToken } from '@/utils/token'
import { isWithoutToken } from './help' import { isWithoutToken } from './help'
import { getUserInfo, signoutRedirect } from '@/utils/oidc/index.js'


export function setupInterceptor(service) { export function setupInterceptor(service) {
service.interceptors.request.use( service.interceptors.request.use(
// 处理不需要token的请求 // 处理不需要token的请求
if (isWithoutToken(config)) { if (isWithoutToken(config)) {
return config return config
} else {
const userInfo = await getUserInfo()
if (userInfo) {
const { token_type, access_token } = userInfo
config.headers.Authorization = `${token_type} ${access_token}`
return config
} else {
signoutRedirect()
return Promise.reject({ response: { status: 401, message: '未登录' }})
}
} }
// const token = getToken()
const token = 'token'
if (token) {
config.headers.Authorization = token
return config
}
/**
* * 未登录或者token过期的情况下
* * 跳转登录页重新登录,携带当前路由及参数,登录成功会回到原来的页面
*/
const { currentRoute } = router
router.replace({
path: '/login',
query: { ...currentRoute.query, redirect: currentRoute.path }
})
return Promise.reject({ code: '-1', message: '未登录' })
}, },
(error) => Promise.reject(error) (error) => Promise.reject(error)
) )
(response) => { (response) => {
const { method } = response?.config const { method } = response?.config
const { code } = response?.data const { code } = response?.data
const { currentRoute } = router
switch (code) { switch (code) {
case 0: case 0:
if (method !== 'get') { if (method !== 'get') {
break break
case 401: case 401:
// 未登录(可能是token过期或者无效了) // 未登录(可能是token过期或者无效了)
removeToken()
router.replace({
path: '/login',
query: { ...currentRoute.query, redirect: currentRoute.path }
})
signoutRedirect()
break break
default: default:
break break
return response?.data return response?.data
}, },
(error) => { (error) => {
return Promise.reject(error)
const { status } = error.response
if (status === 401) {
signoutRedirect()
} else if (status === 403) {
$message.error('暂无权限访问,请联系管理员')
return Promise.reject(error)
} else {
return Promise.reject(error)
}
} }
) )
}
}

+ 91
- 0
src/utils/oidc/index.js View File

import { UserManager } from 'oidc-client'
import { createLocalStorage } from '../cache'
let oidcManager = null

export const initServe = () => {
if (oidcManager) return oidcManager
const { VITE_AUTHORITY, VITE_CLIENT_ID, VITE_CLIENT_SECRET, VITE_REDIRECT_URI } = import.meta.env
oidcManager = new UserManager({
/* 认证服务器 */
authority: VITE_AUTHORITY,
/* 客户端id */
client_id: VITE_CLIENT_ID,
client_secret: VITE_CLIENT_SECRET,
/* 回调客户端页面 */
redirect_uri: VITE_REDIRECT_URI,
post_logout_redirect_uri: VITE_REDIRECT_URI,
response_type: 'code',
/* 授权范围 */
scope: 'openid profile',
automaticSilentRenew: true,
revokeAccessTokenOnSignout: true,
metadata: {
issuer: `${VITE_AUTHORITY}`,
authorization_endpoint: `${VITE_AUTHORITY}/oauth2/authorize`,
token_endpoint: `${VITE_AUTHORITY}/oauth2/token`,
userinfo_endpoint: `${VITE_AUTHORITY}/userinfo`,
end_session_endpoint: `${VITE_AUTHORITY}/toLogout`,
revocation_endpoint: `${VITE_AUTHORITY}/oauth2/revoke`
}
})
return oidcManager
}

export const getUserInfo = async() => {
oidcManager = initServe()
return await oidcManager.getUser()
}
/* 登录 */
export const signinRedirect = async() => {
oidcManager = initServe()
oidcManager.signinRedirect({})
.then(res => {
setPath(window.location.pathname)
console.log('signed in', res)
}).catch(err => {
console.err(err)
})
}
/* 登录回调 */
export const signinRedirectCallback = async() => {
const userInfo = await getUserInfo()
if (!userInfo) {
oidcManager = initServe()
return await oidcManager.signinRedirectCallback()
}
}
/* 退出 */
export const signoutRedirect = () => {
oidcManager = initServe()
oidcManager.signoutRedirect({})
.then(function(res) {
setPath(window.location.pathname)
console.log('signed out', res)
}).catch(function(err) {
console.log(err)
})
}
/* 退出回调 */
export const signoutRedirectCallback = async() => {
const userInfo = await getUserInfo()
if (!userInfo) {
oidcManager = initServe()
return await oidcManager.signoutRedirectCallback()
}
}

const PATH_CODE = 'redirect_path'
const DURATION = 24 * 60 * 60
export const isPath = createLocalStorage()
/* 获取Path */
export function getPath() {
return isPath.get(PATH_CODE)
}
/* 设置Path */
export function setPath(Path, duration = DURATION) {
isPath.set(PATH_CODE, Path, duration)
}
/* 移出Path */
export function removePath() {
isPath.remove(PATH_CODE)
}

+ 49
- 3
src/views/login/index.vue View File

<template> <template>
<div>
<!-- 1 -->
<div class="login">
<n-spin>
<template #description>
正在进入系统中...
</template>
</n-spin>
</div> </div>
</template> </template>


<script> <script>
import { useRoute, useRouter } from 'vue-router'
import { useUserStore } from '@/store/modules/user'
import { signinRedirectCallback, signoutRedirectCallback, signoutRedirect, getPath, removePath } from '@/utils/oidc/index.js'
export default { export default {
name: 'LoginPage', name: 'LoginPage',
setup() { setup() {

const route = useRoute()
const router = useRouter()
const userStore = useUserStore()
if (route.query?.code && route.query?.state) {
signinRedirectCallback().then(res => {
// router.push({ path: getPath() })
// removePath()
// userStore.getUserInfos()
const authority = res.profile.authority
const { VITE_PLATFORM } = import.meta.env
if (authority && (authority.includes(VITE_PLATFORM) || authority.includes('admin'))) {
router.push({ path: '/' })
} else {
$message.error('暂无权限访问,请联系管理员')
setTimeout(() => {
signoutRedirect()
}, 2000)
}
}).catch(err => {
console.log(err)
signoutRedirect()
})
// } else if (getPath()) {
// signoutRedirectCallback().then(res => {
// router.push({ path: getPath() })
// userStore.setUserInfo()
// removePath()
// })
}
} }
} }


</script> </script>
<style scoped lang='scss'> <style scoped lang='scss'>
.login{
width: 100vw;
height: 100vh;
position: relative;
.n-spin-body{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
</style> </style>

Loading…
Cancel
Save