ENV='development'
# base api
VUE_APP_BASE_API='/api'
修改vue.config.js
代理
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'https://xxxxx/',
changeOrigin: true
}
}
}
}
安装axios
npm i axios --save
创建封装文件:src -> utils -> request.js
import axios from 'axios'
import { ElMessage } from 'element-plus'
import store from '@/store'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 5000
})
// 请求拦截器
service.interceptors.request.use(
config => {
// 注入token
if (store.state.user.token) {
// 如果存在token则注入token
config.headers.Authorization = `Bearer ${store.state.user.token}`
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
// 请求成功
response => {
// 根据接口返回情况设置,此处为接口返回包括 success ,message , data
const { success, message, data } = response.data
// 判断当前请求是否成功? 返回解析后的数据 : 失败后消息提示
if (success) {
return data
} else {
// 失败,返回消息提示,使用element消息message模块
ElMessage.error(message)
return Promise.reject(new Error(message))
}
},
// 请求失败 404 500 等
error => {
ElMessage.error(error.message)
return Promise.reject(error)
}
)
export default service
创建api文件夹 -> sys.js
实例
import request from '@/utils/request'
/**
* 登陆
*/
export const login = data => {
return request({
url: '/sys/login',
method: 'POST',
data
})
}
登录动作封装在vuex
的action
中,在store
下创建modules
文件夹,创建user.js
模块,用于处理所有与用户相关的内容。
store -> modules -> user.js
import { login } from '@/api/sys'
export default {
namespaced: true,
state: () => ({
}),
mutations: {
},
actions: {
/**
* 登录请求动作
*/
login (context, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username, password }).then(data => {
resolve(data)
}).catch(err => {
reject(err)
})
})
}
}
}
store -> index.js
import { createStore } from 'vuex'
import user from './modules/user'
export default createStore({
modules: {
user
}
})
在login.vue
中
import { useStore } from 'vuex'
const store = useStore()
const handlelogin = () => {
// 1.表单验证
store.dispatch('user/login' , loginForm.value).then(()=>{
// 2.登录后处理
})
}
通常获取到token
之后使用localStorage
或Vuex
保存在 Localstorage
是为了方便实现 自动登录功能
保存在 vuex
中是为了后面在其他位置进行使用
utils -> storage.js
/**
* 存储数据
*/
export const setItem = (key, value) => {
// value 分为两种情况:
// 1.基数据类型
// 2.复杂数据类型
if (typeof value === 'object') {
value = JSON.stringify(value)
}
window.localStorage.setItem(key, value)
}
/**
* 获取数据
*/
export const getItem = key => {
const data = window.localStorage.getItem(key)
try {
return JSON.parse(data)
} catch (err) {
return data
}
}
/**
* 存储数据
*/
export const removeItem = key => {
window.localStorage.removeItem(key)
}
/**
* 存储数据
*/
export const removeAllItem = () => {
window.localStorage.clear()
}
使用案例:store-> modules -> user.js
import { login } from '@/api/sys'
import { setItem, getItem } from '@/utils/storage'
export default {
namespaced: true,
state: () => ({
token: getItem('token') || ''
}),
mutations: {
setToken (state, token) {
state.token = token
setItem('token', token)
}
},
actions: {
/**
* 登录请求动作
*/
login (context, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username, password }).then(data => {
this.commit('user/setToken', data.token)
resolve(data)
}).catch(err => {
reject(err)
})
})
}
}
}
当用户未登陆时,不允许进入除 login 之外的其他页面
用户登陆后,token 未过期之前,不允许进入 login 页面
而想要实现这个功能,那么最好的方式就是通过 路由守卫
来进行实现
在main.js
平级创建permission.js
文件
import router from '@/router'
import store from '@/store'
// 白名单,可以包括404,500等页面
const whiteList = ['/login']
/**
* 路由前置守卫
*/
router.beforeEach((to, from, next) => {
if (store.state.user.token) {
// 1.用户已登录,不允许进入login
if (to.path === '/login') {
next('/')
} else {
next()
}
} else {
// 2.用户未登录,只允许进入login
if (whiteList.indexOf(to.path) > -1) {
// 在白名单,直接通过
next()
} else {
next('/login')
}
}
})
那么无论是什么退出方式,在用户退出时,所需要执行的操作都是固定的:
在 store/modules/user.js
中,添加对应action
logout () {
this.commit('user/setToken', '')
this.commit('user/setUserInfo', {})
removeAllItem()
// TODO: 清理权限相关
router.push('/')
}
退出时调用该方法即可
用户被动退出的场景主要有两个:
- token 失效
- 单点登录:其他人登录该账号被“顶下来"
那么这两种场景下,在前端对应的处理方案一共也分为两种,共分为 主动处理、被动处理 两种:
- 主动处理:主要应对 token 失效
- 被动处理:同时应对 token 失效与单点登录
1.主动处理
此时用到的是“时效token”,对于 token 本身是拥有时效的,但是通常情况下这个时效都是在服务端进行处理。而此时我们要在服务端处理token时效的同时在前端主动介入token’时效的处理中。 从而保证用户信息的更加安全性。
对应到代码中的实现方案为
创建 utils/auth.js
文件,并写入以下代码
import { getItem, setItem } from './storage'
const TIME_STAMP = 'timeStamp'
const TOKEN_TIMEOUT_VALUE = 2 * 3600 * 1000
/**
* 获取时间戳
*/
export function getTimeStamp () {
return getItem(TIME_STAMP)
}
/**
*设置时间戳
*/
export function setTimeStamp () {
setItem(TIME_STAMP, Date.now())
}
/**
*是否超时
*/
export function isCheckTimeout () {
// 当前时间
const currentTime = Date.now()
// 缓存时间
const timeStamp = getTimeStamp()
return currentTime - timeStamp > TOKEN_TIMEOUT_VALUE
}
登录成功后设置时间戳,保存登录时间 -> 跳转页面 ,在每次请求接口时检查时间是否超时:
2. 被动处理
在utils/reques
t的响应拦截器中增加一下逻辑:
// 响应拦截器
service.interceptors.response.use(
// 请求成功
response => {
// 根据接口返回情况设置,此处为接口返回包括 success ,message , data
const { success, message, data } = response.data
// 判断当前请求是否成功? 返回解析后的数据 : 失败后消息提示
if (success) {
return data
} else {
// 失败,返回消息提示,使用element消息message模块
ElMessage.error(message)
return Promise.reject(new Error(message))
}
},
// 请求失败 404 500 等
error => {
if (error.response && error.response.data && error.response.data.code === 401) {
store.dispatch('user/logout')
}
ElMessage.error(error.message)
return Promise.reject(error)
}
)