forked from AkkomaGang/admin-fe
Authorization
This commit is contained in:
parent
83fc9e6aa8
commit
96e52a14cc
11 changed files with 67 additions and 153 deletions
|
@ -57,7 +57,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
|
||||||
template: 'index.html',
|
template: 'index.html',
|
||||||
inject: true,
|
inject: true,
|
||||||
favicon: resolve('favicon.ico'),
|
favicon: resolve('favicon.ico'),
|
||||||
title: 'vue-element-admin',
|
title: 'Admin FE',
|
||||||
templateParameters: {
|
templateParameters: {
|
||||||
BASE_URL: config.dev.assetsPublicPath + config.dev.assetsSubDirectory,
|
BASE_URL: config.dev.assetsPublicPath + config.dev.assetsSubDirectory,
|
||||||
},
|
},
|
||||||
|
|
|
@ -55,7 +55,7 @@ const webpackConfig = merge(baseWebpackConfig, {
|
||||||
template: 'index.html',
|
template: 'index.html',
|
||||||
inject: true,
|
inject: true,
|
||||||
favicon: resolve('favicon.ico'),
|
favicon: resolve('favicon.ico'),
|
||||||
title: 'vue-element-admin',
|
title: 'Admin FE',
|
||||||
templateParameters: {
|
templateParameters: {
|
||||||
BASE_URL: config.build.assetsPublicPath + config.build.assetsSubDirectory,
|
BASE_URL: config.build.assetsPublicPath + config.build.assetsSubDirectory,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
NODE_ENV: '"development"',
|
NODE_ENV: '"development"',
|
||||||
ENV_CONFIG: '"dev"',
|
ENV_CONFIG: '"dev"',
|
||||||
BASE_API: '"https://api-dev"'
|
BASE_API: '"http://localhost:4000"'
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<meta name="renderer" content="webkit">
|
<meta name="renderer" content="webkit">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||||
<title>vue-element-admin</title>
|
<title>Admin FE</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script src=<%= BASE_URL %>/tinymce4.7.5/tinymce.min.js></script>
|
<script src=<%= BASE_URL %>/tinymce4.7.5/tinymce.min.js></script>
|
||||||
|
|
|
@ -1,29 +1,42 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
export function loginByUsername(username, password) {
|
export async function loginByUsername(username, password) {
|
||||||
const data = {
|
const appsRequest = await request({
|
||||||
username,
|
url: '/api/v1/apps',
|
||||||
password
|
|
||||||
}
|
|
||||||
return request({
|
|
||||||
url: '/login/login',
|
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data: {
|
||||||
|
client_name: `AdminFE_${Math.random()}`,
|
||||||
|
redirect_uris: `${window.location.origin}/oauth-callback`,
|
||||||
|
scopes: 'read write follow'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const app = appsRequest.data
|
||||||
|
|
||||||
|
return request({
|
||||||
|
url: '/oauth/token',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
client_id: app.client_id,
|
||||||
|
client_secret: app.client_secret,
|
||||||
|
grant_type: 'password',
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logout() {
|
export function getUserInfo() {
|
||||||
return request({
|
return request({
|
||||||
url: '/login/logout',
|
url: '/api/account/verify_credentials',
|
||||||
method: 'post'
|
method: 'post'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUserInfo(token) {
|
export function logout() {
|
||||||
return request({
|
|
||||||
url: '/user/info',
|
|
||||||
method: 'get',
|
|
||||||
params: { token }
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const oauth = { loginByUsername, getUserInfo, logout }
|
||||||
|
|
||||||
|
export default oauth
|
||||||
|
|
|
@ -63,7 +63,8 @@ export default {
|
||||||
theme: 'Theme',
|
theme: 'Theme',
|
||||||
clipboardDemo: 'Clipboard',
|
clipboardDemo: 'Clipboard',
|
||||||
i18n: 'I18n',
|
i18n: 'I18n',
|
||||||
externalLink: 'External Link'
|
externalLink: 'External Link',
|
||||||
|
users: 'Users'
|
||||||
},
|
},
|
||||||
navbar: {
|
navbar: {
|
||||||
logOut: 'Log Out',
|
logOut: 'Log Out',
|
||||||
|
|
|
@ -26,7 +26,7 @@ router.beforeEach((to, from, next) => {
|
||||||
} else {
|
} else {
|
||||||
if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
|
if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
|
||||||
store.dispatch('GetUserInfo').then(res => { // 拉取user_info
|
store.dispatch('GetUserInfo').then(res => { // 拉取user_info
|
||||||
const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop']
|
const roles = res.data.rights.admin ? ['admin'] : []
|
||||||
store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表
|
store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表
|
||||||
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
|
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
|
||||||
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
|
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
|
||||||
|
|
|
@ -76,32 +76,6 @@ export const constantRouterMap = [
|
||||||
meta: { title: 'dashboard', icon: 'dashboard', noCache: true, affix: true }
|
meta: { title: 'dashboard', icon: 'dashboard', noCache: true, affix: true }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/documentation',
|
|
||||||
component: Layout,
|
|
||||||
redirect: '/documentation/index',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'index',
|
|
||||||
component: () => import('@/views/documentation/index'),
|
|
||||||
name: 'Documentation',
|
|
||||||
meta: { title: 'documentation', icon: 'documentation', affix: true }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/guide',
|
|
||||||
component: Layout,
|
|
||||||
redirect: '/guide/index',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'index',
|
|
||||||
component: () => import('@/views/guide/index'),
|
|
||||||
name: 'Guide',
|
|
||||||
meta: { title: 'guide', icon: 'guide', noCache: true }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -112,6 +86,18 @@ export default new Router({
|
||||||
})
|
})
|
||||||
|
|
||||||
export const asyncRouterMap = [
|
export const asyncRouterMap = [
|
||||||
|
{
|
||||||
|
path: '/users',
|
||||||
|
component: Layout,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'index',
|
||||||
|
component: () => import('@/views/users/index'),
|
||||||
|
name: 'Users',
|
||||||
|
meta: { title: 'users', icon: 'peoples', noCache: true }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/permission',
|
path: '/permission',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
|
|
|
@ -44,14 +44,13 @@ const user = {
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
// 用户名登录
|
|
||||||
LoginByUsername({ commit }, userInfo) {
|
LoginByUsername({ commit }, userInfo) {
|
||||||
const username = userInfo.username.trim()
|
const username = userInfo.username.trim()
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
loginByUsername(username, userInfo.password).then(response => {
|
loginByUsername(username, userInfo.password).then(response => {
|
||||||
const data = response.data
|
const data = response.data
|
||||||
commit('SET_TOKEN', data.token)
|
commit('SET_TOKEN', data.access_token)
|
||||||
setToken(response.data.token)
|
setToken(response.data.access_token)
|
||||||
resolve()
|
resolve()
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
reject(error)
|
reject(error)
|
||||||
|
@ -59,25 +58,25 @@ const user = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取用户信息
|
|
||||||
GetUserInfo({ commit, state }) {
|
GetUserInfo({ commit, state }) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getUserInfo(state.token).then(response => {
|
getUserInfo(state.token).then(response => {
|
||||||
// 由于mockjs 不支持自定义状态码只能这样hack
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
reject('Verification failed, please login again.')
|
reject('Verification failed, please login again.')
|
||||||
}
|
}
|
||||||
const data = response.data
|
const data = response.data
|
||||||
|
|
||||||
if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
if (data.rights) {
|
||||||
commit('SET_ROLES', data.roles)
|
if (data.rights.admin) {
|
||||||
|
commit('SET_ROLES', ['admin'])
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
reject('getInfo: roles must be a non-null array!')
|
reject('getInfo: roles must be a non-null array!')
|
||||||
}
|
}
|
||||||
|
|
||||||
commit('SET_NAME', data.name)
|
commit('SET_NAME', data.name)
|
||||||
commit('SET_AVATAR', data.avatar)
|
commit('SET_AVATAR', data.profile_image_url)
|
||||||
commit('SET_INTRODUCTION', data.introduction)
|
commit('SET_INTRODUCTION', '')
|
||||||
resolve(response)
|
resolve(response)
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
reject(error)
|
reject(error)
|
||||||
|
|
|
@ -12,10 +12,8 @@ const service = axios.create({
|
||||||
// request interceptor
|
// request interceptor
|
||||||
service.interceptors.request.use(
|
service.interceptors.request.use(
|
||||||
config => {
|
config => {
|
||||||
// Do something before request is sent
|
|
||||||
if (store.getters.token) {
|
if (store.getters.token) {
|
||||||
// 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
|
config.headers['Authorization'] = `Bearer ${getToken()}`
|
||||||
config.headers['X-Token'] = getToken()
|
|
||||||
}
|
}
|
||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left">
|
<el-form ref="loginForm" :model="loginForm" class="login-form" auto-complete="on" label-position="left">
|
||||||
<div class="title-container">
|
<div class="title-container">
|
||||||
<h3 class="title">
|
<h3 class="title">
|
||||||
{{ $t('login.title') }}
|
{{ $t('login.title') }}
|
||||||
</h3>
|
</h3>
|
||||||
<lang-select class="set-language" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-form-item prop="username">
|
<el-form-item prop="username">
|
||||||
|
@ -41,66 +40,18 @@
|
||||||
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">
|
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">
|
||||||
{{ $t('login.logIn') }}
|
{{ $t('login.logIn') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<div style="position:relative">
|
|
||||||
<div class="tips">
|
|
||||||
<span>{{ $t('login.username') }} : admin</span>
|
|
||||||
<span>{{ $t('login.password') }} : {{ $t('login.any') }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="tips">
|
|
||||||
<span style="margin-right:18px;">
|
|
||||||
{{ $t('login.username') }} : editor
|
|
||||||
</span>
|
|
||||||
<span>{{ $t('login.password') }} : {{ $t('login.any') }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-button class="thirdparty-button" type="primary" @click="showDialog=true">
|
|
||||||
{{ $t('login.thirdparty') }}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<el-dialog :title="$t('login.thirdparty')" :visible.sync="showDialog">
|
|
||||||
{{ $t('login.thirdpartyTips') }}
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<social-sign />
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { isvalidUsername } from '@/utils/validate'
|
|
||||||
import LangSelect from '@/components/LangSelect'
|
|
||||||
import SocialSign from './socialsignin'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
components: { LangSelect, SocialSign },
|
data: function() {
|
||||||
data() {
|
|
||||||
const validateUsername = (rule, value, callback) => {
|
|
||||||
if (!isvalidUsername(value)) {
|
|
||||||
callback(new Error('Please enter the correct user name'))
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const validatePassword = (rule, value, callback) => {
|
|
||||||
if (value.length < 6) {
|
|
||||||
callback(new Error('The password can not be less than 6 digits'))
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
loginForm: {
|
loginForm: {
|
||||||
username: 'admin',
|
username: '',
|
||||||
password: '1111111'
|
password: ''
|
||||||
},
|
|
||||||
loginRules: {
|
|
||||||
username: [{ required: true, trigger: 'blur', validator: validateUsername }],
|
|
||||||
password: [{ required: true, trigger: 'blur', validator: validatePassword }]
|
|
||||||
},
|
},
|
||||||
passwordType: 'password',
|
passwordType: 'password',
|
||||||
loading: false,
|
loading: false,
|
||||||
|
@ -116,12 +67,6 @@ export default {
|
||||||
immediate: true
|
immediate: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
|
||||||
// window.addEventListener('hashchange', this.afterQRScan)
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
// window.removeEventListener('hashchange', this.afterQRScan)
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
showPwd() {
|
showPwd() {
|
||||||
if (this.passwordType === 'password') {
|
if (this.passwordType === 'password') {
|
||||||
|
@ -131,47 +76,19 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleLogin() {
|
handleLogin() {
|
||||||
this.$refs.loginForm.validate(valid => {
|
this.loading = true
|
||||||
if (valid) {
|
this.$store.dispatch('LoginByUsername', this.loginForm).then(() => {
|
||||||
this.loading = true
|
this.loading = false
|
||||||
this.$store.dispatch('LoginByUsername', this.loginForm).then(() => {
|
this.$router.push({ path: this.redirect || '/' })
|
||||||
this.loading = false
|
}).catch(() => {
|
||||||
this.$router.push({ path: this.redirect || '/' })
|
this.loading = false
|
||||||
}).catch(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
console.log('error submit!!')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
|
||||||
afterQRScan() {
|
|
||||||
// const hash = window.location.hash.slice(1)
|
|
||||||
// const hashObj = getQueryObject(hash)
|
|
||||||
// const originUrl = window.location.origin
|
|
||||||
// history.replaceState({}, '', originUrl)
|
|
||||||
// const codeMap = {
|
|
||||||
// wechat: 'code',
|
|
||||||
// tencent: 'code'
|
|
||||||
// }
|
|
||||||
// const codeName = hashObj[codeMap[this.auth_type]]
|
|
||||||
// if (!codeName) {
|
|
||||||
// alert('第三方登录失败')
|
|
||||||
// } else {
|
|
||||||
// this.$store.dispatch('LoginByThirdparty', codeName).then(() => {
|
|
||||||
// this.$router.push({ path: '/' })
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style rel="stylesheet/scss" lang="scss">
|
<style rel="stylesheet/scss" lang="scss">
|
||||||
/* 修复input 背景不协调 和光标变色 */
|
|
||||||
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
|
|
||||||
|
|
||||||
$bg:#283443;
|
$bg:#283443;
|
||||||
$light_gray:#eee;
|
$light_gray:#eee;
|
||||||
$cursor: #fff;
|
$cursor: #fff;
|
||||||
|
|
Loading…
Reference in a new issue