vue-element-admin 是一款基于vue和element-ui实现的开箱即用的后台管理系统。
vue-element-admin 是一款基于vue和element-ui实现的开箱即用的后台管理系统,本文探讨如何实现vue-element-admin与Springboot后端项目完美整合,实现真正的前后端分离的项目架构。
1.改造vue-element-admin登录流程为调用后端真实接口
vue-element-admin的登录流程默认是使用了mock模拟接口实现的。
1.实现后端接口
以国内比较流行sa-token权限框架实现为例,后端首先需要实现三个接口。分别是用户登录接口,获取用户信息接口和用户注销接口。
1).用户登录接口URL为:
http://localhost:8090/web-project/users/auth?username=管理员&password=123456
返回的json数据格式必须如下:
其中必须返回tokenName和tokenValue,实现前后端分离的登录权限认证。
{
"data": {
"createTime": "2023-03-13 14:00:45",
"modifyTime": "2023-03-13 14:00:45",
"flag": false,
"version": 1,
"uid": 1,
"username": "管理员",
"tokenName": "satoken",
"tokenValue": "8fcf77ca-c52a-4785-a2f4-de62cd1536f2"
},
"msg": "登录成功!",
"code": 200
}
2).获取用户信息接口。
获取用户信息接口URL为:
http://localhost:8090/web-project/users/isLogin
返回的json数据格式必须如下:
该接口用于前端每次访问需要登录权限的资源时,都必须发送该请求以验证用户身份是否合法,返回状态码200表示该用户已经登录,并且返回该用户的角色信息(roles)。
{
"data": {
"createTime": "2023-03-13 14:00:45",
"modifyTime": "2023-03-13 14:00:45",
"flag": false,
"version": 1,
"uid": 1,
"username": "管理员",
"password": "",
"avatar": "https://img.simoniu.com/vue-color-avatar11.png",
"introduce": "我是管理员",
"tokenName": "satoken",
"tokenValue": "8fcf77ca-c52a-4785-a2f4-de62cd1536f2",
"roles": [
"admin"
]
},
"msg": "已登录",
"code": 200
}
3).用户注销接口
用户注销接口URL:
http://localhost:8090/web-project/users/logout
返回的json数据格式必须如下:
{
"data": null,
"msg": "注销成功!",
"code": 200
}
2.前端调用后端真实接口
这里需要修改vue-element-admin的四个js模块。分别是:
api/user.js 修改如下:
import request from '@/utils/request'
export function login(username, password) {
return request({
url: 'http://localhost:8090/web-project/users/auth?username=' + username + '&password=' + password,
method: 'get'
})
}
export function getInfo(token) {
return request({
url: 'http://localhost:8090/web-project/users/isLogin',
method: 'get'
})
}
export function logout() {
return request({
url: 'http://localhost:8090/web-project/users/logout',
method: 'get'
})
}
store/modules/user.js 修改如下:
import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import router, { resetRouter } from '@/router'
const state = {
token: getToken(),
name: '',
avatar: '',
introduction: '',
roles: []
}
const mutations = {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_INTRODUCTION: (state, introduction) => {
state.introduction = introduction
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
}
}
const actions = {
login({ commit }, userInfo) {
console.log('----store/user/login-----------')
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login(username.trim(), password).then(response => {
const { data } = response
commit('SET_TOKEN', data.tokenValue)
setToken(data.tokenValue)
resolve()
}).catch(error => {
reject(error)
})
})
},
// get user info
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(response => {
const { data } = response
if (!data) {
reject('Verification failed, please Login again.')
}
const { roles, username, avatar } = data
// roles must be a non-empty array
if (!roles || roles.length <= 0) {
reject('getInfo: roles must be a non-null array!')
}
commit('SET_ROLES', roles)
commit('SET_NAME', username)
commit('SET_AVATAR', avatar)
// commit('SET_INTRODUCTION', introduction)
resolve(data)
}).catch(error => {
reject(error)
})
})
},
// user logout
logout({ commit, state, dispatch }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
resetRouter()
// reset visited views and cached views
// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
dispatch('tagsView/delAllViews', null, { root: true })
resolve()
}).catch(error => {
reject(error)
})
})
},
// remove token
resetToken({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
resolve()
})
},
// dynamically modify permissions
async changeRoles({ commit, dispatch }, role) {
const token = role + '-token'
commit('SET_TOKEN', token)
setToken(token)
const { roles } = await dispatch('getInfo')
resetRouter()
// generate accessible routes map based on roles
const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
// dynamically add accessible routes
router.addRoutes(accessRoutes)
// reset visited views and cached views
dispatch('tagsView/delAllViews', null, { root: true })
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
utils/auth.js 修改如下:
import Cookies from 'js-cookie'
// const TokenKey = 'Admin-Token'
const TokenKey = 'satoken'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
utils/request.js 主要修改请求拦截器和响应状态码的值:
// request interceptor
service.interceptors.request.use(
config => {
// do something before request is sent
if (store.getters.token) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
//把satoken添加到请求头部
config.headers['satoken'] = getToken()
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
...
response => {
const res = response.data
// if the custom code is not 20000, it is judged as an error.
//由于后端接口状态码200表示成功,这里把20000修改为200
if (res.code !== 200) {
Message({
message: res.msg || 'Error', //后端返回的消息属性也改为msg
type: 'error',
duration: 5 * 1000
})
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.msg || 'Error'))
} else {
return res
}
},
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
至此,改造vue-element-admin登录流程为调用后端真实接口就真正实现了!