@@ -4,5 +4,6 @@ VITE_APP_TITLE = '智飞' | |||
# 端口号 | |||
VITE_PORT = 3050 | |||
VITE_SERVER = "/pilot/admin" | |||
VITE_SERVER = "/pilot/admin" | |||
VITE_PLATFORM = "tuoheng-dsp-web" |
@@ -5,7 +5,12 @@ VITE_PUBLIC_PATH = '/' | |||
VITE_APP_USE_MOCK = false | |||
# proxy | |||
VITE_PROXY = [["/api","http://192.168.11.11:9055"]] | |||
VITE_PROXY = [["","http://192.168.11.11:7011"]] | |||
# 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' |
@@ -11,4 +11,9 @@ VITE_PROXY = [["/api-local","http://127.0.0.1:8002"],["/api-mock","http://127.0. | |||
VITE_APP_GLOB_BASE_API = '/api-local' | |||
# 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' |
@@ -8,4 +8,9 @@ VITE_APP_USE_MOCK = false | |||
VITE_PROXY = [["/api","http://127.0.0.1:8002/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' |
@@ -5,7 +5,12 @@ VITE_PUBLIC_PATH = '/' | |||
VITE_APP_USE_MOCK = false | |||
# proxy | |||
VITE_PROXY = [["","http://192.168.11.11:9055"]] | |||
VITE_PROXY = [["","http://192.168.11.241:9089"]] | |||
# 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' |
@@ -18,6 +18,7 @@ | |||
"chart-all": "^1.0.2", | |||
"dayjs": "^1.11.2", | |||
"mockjs": "^1.1.0", | |||
"oidc-client": "^1.11.5", | |||
"pinia": "^2.0.13", | |||
"pinia-plugin-persist": "^1.0.0", | |||
"tinymce": "^5.10.2", |
@@ -19,7 +19,7 @@ export function userLogin(data = {}) { | |||
*/ | |||
export function getUser() { | |||
return request({ | |||
url: '/index/getUserInfo', | |||
url: '/user/getUserInfo', | |||
method: 'get' | |||
}) | |||
} |
@@ -4,16 +4,17 @@ | |||
<n-image height="18" src="/logo.png" preview-disabled /> | |||
</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"> | |||
<n-image | |||
<!-- <n-image | |||
class="user_avatar" | |||
:src="userInfo.avatar" | |||
:src="getUserInfo.avatar" | |||
preview-disabled | |||
/> | |||
<span class="user_name">{{ userInfo.realname }}</span> | |||
/> --> | |||
<span class="user_name">{{ getUserInfo.realname }}</span> | |||
</div> | |||
</n-dropdown> | |||
<div v-if="!getUserInfo.hasLogin" class="header__login" @click="handleLogin">登录</div> | |||
</n-layout-header> | |||
</template> | |||
@@ -24,6 +25,7 @@ import { EditOutlined, LogoutOutlined } from '@vicons/antd' | |||
import { renderIcon } from '@/utils' | |||
import { useUserStore } from '@/store/modules/user.js' | |||
import { useSettingStore } from '@/store/modules/setting.js' | |||
import { signinRedirect, signoutRedirect } from '@/utils/oidc/index.js' | |||
export default defineComponent({ | |||
name: 'LayoutHeader', | |||
setup() { | |||
@@ -33,11 +35,11 @@ export default defineComponent({ | |||
const settingStore = useSettingStore() | |||
const data = reactive({ | |||
options: [ | |||
{ | |||
label: '修改密码', | |||
key: 'edit', | |||
icon: renderIcon(EditOutlined) | |||
}, | |||
// { | |||
// label: '修改密码', | |||
// key: 'edit', | |||
// icon: renderIcon(EditOutlined) | |||
// }, | |||
{ | |||
label: '退出登录', | |||
key: 'out', | |||
@@ -57,21 +59,29 @@ export default defineComponent({ | |||
async function handleSelect(key) { | |||
switch (key) { | |||
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 { | |||
...toRefs(data), | |||
getLogoWidth, | |||
handleSelect | |||
getUserInfo, | |||
handleSelect, | |||
handleLogin | |||
} | |||
} | |||
}) |
@@ -37,10 +37,10 @@ const settingStore = useSettingStore() | |||
const menuMode = computed(() => settingStore.getMenuMode) | |||
const tagsMenuSetting = computed(() => settingStore.getTagsMenuSetting) | |||
const useUser = useUserStore() | |||
function getUserNow() { | |||
useUser.getUserInfo() | |||
} | |||
getUserNow() | |||
// function getUserNow() { | |||
// useUser.getUserInfo() | |||
// } | |||
// getUserNow() | |||
</script> | |||
@@ -1,44 +1,39 @@ | |||
import { useUserStore } from '@/store/modules/user' | |||
import { usePermissionStore } from '@/store/modules/permission' | |||
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) { | |||
const userStore = useUserStore() | |||
const permissionStore = usePermissionStore() | |||
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 { | |||
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 { | |||
if (WHITE_LIST.includes(to.path)) { | |||
if (to.path === '/login') { | |||
next() | |||
} else { | |||
next({ path: '/login', query: { ...to.query, redirect: to.path }}) | |||
signinRedirect() | |||
} | |||
} | |||
}) |
@@ -1,71 +1,56 @@ | |||
import { defineStore } from 'pinia' | |||
import { userLogin, loginOut } from '@/api/login' | |||
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', { | |||
persist: true, | |||
persist: { | |||
enabled: true | |||
}, | |||
state() { | |||
return { | |||
userInfo: {} | |||
userInfo: { | |||
hasLogin: false | |||
} | |||
} | |||
}, | |||
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: { | |||
/* 登录 */ | |||
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 { | |||
const res = await loginOut() | |||
const res = await getUser() | |||
if (res.code === 0) { | |||
removeToken() | |||
this.reset() | |||
return Promise.resolve(res) | |||
this.setUserInfo({ hasLogin: true, ...res.data }) | |||
return Promise.resolve(res.data.type) | |||
} else { | |||
return Promise.reject(res) | |||
this.setUserInfo() | |||
} | |||
} 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 } | |||
} | |||
} |
@@ -1,6 +1,5 @@ | |||
import { router } from '@/router' | |||
import { getToken, removeToken } from '@/utils/token' | |||
import { isWithoutToken } from './help' | |||
import { getUserInfo, signoutRedirect } from '@/utils/oidc/index.js' | |||
export function setupInterceptor(service) { | |||
service.interceptors.request.use( | |||
@@ -12,23 +11,17 @@ export function setupInterceptor(service) { | |||
// 处理不需要token的请求 | |||
if (isWithoutToken(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) | |||
) | |||
@@ -37,7 +30,6 @@ export function setupInterceptor(service) { | |||
(response) => { | |||
const { method } = response?.config | |||
const { code } = response?.data | |||
const { currentRoute } = router | |||
switch (code) { | |||
case 0: | |||
if (method !== 'get') { | |||
@@ -49,11 +41,7 @@ export function setupInterceptor(service) { | |||
break | |||
case 401: | |||
// 未登录(可能是token过期或者无效了) | |||
removeToken() | |||
router.replace({ | |||
path: '/login', | |||
query: { ...currentRoute.query, redirect: currentRoute.path } | |||
}) | |||
signoutRedirect() | |||
break | |||
default: | |||
break | |||
@@ -61,7 +49,15 @@ export function setupInterceptor(service) { | |||
return response?.data | |||
}, | |||
(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) | |||
} | |||
} | |||
) | |||
} | |||
} |
@@ -0,0 +1,91 @@ | |||
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) | |||
} |
@@ -1,17 +1,63 @@ | |||
<template> | |||
<div> | |||
<!-- 1 --> | |||
<div class="login"> | |||
<n-spin> | |||
<template #description> | |||
正在进入系统中... | |||
</template> | |||
</n-spin> | |||
</div> | |||
</template> | |||
<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 { | |||
name: 'LoginPage', | |||
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> | |||
<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> |