登录方案记录
需求:根据当前环境的不同,请求不同的 BaseUrl
解决:在根目录中新建.env.development与.env.production连个文件,进行配置:
# .env.production ENV = 'production' # base api VUE_APP_BASE_API = '/prod-api' # .env.development ENV = 'development' # base api VUE_APP_BASE_API = '/api'
讯享网
然后在utils/request.js中配置:
讯享网import axios from 'axios' const service = axios.create({ //process.env 表示当前环境下的所有环境变量 baseURL: process.env.VUE_APP_BASE_API, timeout: 5000 }) export default service
问题:
出现这个问题的原因,是因为我们在前面配置环境变量时指定了 开发环境下,请求的 BaseUrl 为 /api;这样的一个请求会被自动键入到当前前端所在的服务中,所以我们最终就得到了 http://192.168.18.42:8081/api/sys/login 这样的一个请求路径
解决:通过指定 webpack DevServer 代理 的形式,代理当前的 url 请求;在vue.config.js中进行如下编辑
module.exports = { devServer: { // 配置反向代理 proxy: { // 当地址中有/api的时候会触发代理机制 '/api': { // 要代理的服务器地址 这里不用写 api target: 'https://api.xxxxxxx.club/', changeOrigin: true // 是否跨域 } } }, xxxxxxxxx }
在vuex中进行请求处理
创建 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 模块,进行请求处理:
import { login } from '@/api/sys' import md5 from 'md5' export default { namespaced: true, state: () => ({}), mutations: {}, actions: { login(context, userInfo) { const { username, password } = userInfo return new Promise((resolve, reject) => { login({ username, // md5用来将密码加密处理 password: md5(password) }) .then(data => { resolve() }) .catch(err => { reject(err) }) }) } } }
在 store/index 中完成注册:
讯享网import { createStore } from 'vuex' import user from './modules/user.js' export default createStore({ modules: { user } })
在 login 中,触发定义的 action :
<template>xxxxxx</template> <script setup> import { useStore } from "vuex"; const store = useStore(); const handleLogin = () => { //先校验数据,校验省略,数据格式校验通过进行登录 store .dispatch("user/login", loginForm.value) .then(() => { // TODO: 登录后操作 }) .catch((err) => { console.log(err); }); }; </script>
本地缓存处理方案
创建 utils/storage.js 文件,封装对本地LocalStorage的操作:注意两种不同数据类型的处理
讯享网/ * 存储数据 */ export const setItem = (key, value) => { // 将数组、对象类型的数据转化为 JSON 字符串进行存储 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 = key => { window.localStorage.clear() }
创建 constant 常量目录 constant/index.js :
export const TOKEN = 'token'
在 vuex 的 user 模块下,处理 token 的保存:
讯享网import { login } from '@/api/sys' import md5 from 'md5' import { setItem, getItem } from '@/utils/storage' import { TOKEN } from '@/constant' export default { namespaced: true, state: () => ({ token: getItem(TOKEN) || '' }), mutations: { setToken(state, token) { state.token = token setItem(TOKEN, token) } }, actions: { login(context, userInfo) { ... .then(data => { this.commit('user/setToken', data.data.data.token) resolve() }) ... }) } } }
通过axios响应拦截器处理data.data.data.token问题。在 utils/request.js 中编码:
先放上响应的数据结果:


import axios from 'axios' import { ElMessage } from 'element-plus' ... // 响应拦截器 service.interceptors.response.use( response => { const { success, message, data } = response.data // 要根据success的成功与否决定下面的操作 if (success) { return data } else { // 业务错误 ElMessage.error(message) // 提示错误消息 return Promise.reject(new Error(message)) } }, error => { // TODO: 将来处理 token 超时问题 ElMessage.error(error.message) // 提示错误信息 return Promise.reject(error) } ) export default service
再修改下vuex 中的 user 模块 :
讯享网this.commit('user/setToken', data.token)
登录鉴权
当用户未登陆时,不允许进入除 login 之外的其他页面。用户登录后,token 未过期之前,不允许进入 login 页面
根目录创建permission.js,并在main.js中导入:
import router from './router' import store from './store' // 白名单 const whiteList = ['/login'] / * 路由前置守卫 */ router.beforeEach(async (to, from, next) => { // 存在 token ,进入主页 // if (store.state.user.token) { // 快捷访问 if (store.getters.token) { if (to.path === '/login') { next('/') } else { next() } } else { // 没有token的情况下,可以进入白名单 if (whiteList.indexOf(to.path) > -1) { next() } else { next('/login') } } })
在此处的 getters 被当作 快捷访问 的形式进行访问,所以我们需要声明对应的模块,创建 store/getters
讯享网const getters = { token: state => state.user.token } export default getters
在 store/index 中进行导入:
import getters from './getters' export default createStore({ getters, ... })
退出登录方案记录
退出登录 它的触发时机一般有两种:
- 用户主动退出:用户点击登录按钮之后退出
- 用户被动退出:
token过期或被 其他人”顶下来“ 时退出
实现用户主动退出的对应策略
在 store/modules/user.js 中,添加对应 action :
讯享网import router from '@/router' logout() { //一、清理掉当前用户缓存数据 this.commit('user/setToken', '') this.commit('user/setUserInfo', {}) removeAllItem() //二、清理掉权限相关配置 //三、返回到登录页 router.push('/login') }
为退出登录按钮添加点击事件,触发 logout 的 action :
import { useStore } from 'vuex' const store = useStore() const logout = () => { store.dispatch('user/logout') }
实现用户被动退出的对应策略之主动处理(应对 token 失效 )
在 constant 中声明对应常量
讯享网// token 时间戳 export const TIME_STAMP = 'timeStamp' // 超时时长(毫秒) 两小时 export const TOKEN_TIMEOUT_VALUE = 2 * 3600 * 1000
在用户登陆时,记录当前登录时间。创建 utils/auth.js 文件
import { TIME_STAMP, TOKEN_TIMEOUT_VALUE } from '@/constant' import { setItem, getItem } from '@/utils/storage' / * 获取时间戳 */ export function getTimeStamp() { return getItem(TIME_STAMP) } / * 设置时间戳 */ export function setTimeStamp() { setItem(TIME_STAMP, Date.now()) } / * 是否超时 */ export function isCheckTimeout() { // 当前时间戳 var currentTime = Date.now() // 缓存时间戳 var timeStamp = getTimeStamp() return currentTime - timeStamp > TOKEN_TIMEOUT_VALUE }
在用户登录成功之后去设置时间,到 store/user.js 的 login 中:
讯享网import { setTimeStamp } from '@/utils/auth' login(context, userInfo) { ... return new Promise((resolve, reject) => { ... .then(data => { ... // 保存登录时间 setTimeStamp() resolve() }) }) }
在 utils/request 对应的请求拦截器中进行主动介入
import { isCheckTimeout } from '@/utils/auth' if (store.getters.token) { //判断是否过期 if (isCheckTimeout()) { // 登出操作 store.dispatch('user/logout') return Promise.reject(new Error('token 失效')) } ... }
实现用户被动退出的对应策略之被动处理(应对 token 失效 )
在 utils/request 的响应拦截器中,通过服务端返回的状态码进行判断:
讯享网// 响应拦截器 service.interceptors.response.use( response => { ... }, error => { // 处理 token 超时问题 if ( error.response && error.response.data && error.response.data.code === 401 ) { // token超时 store.dispatch('user/logout') } ElMessage.error(error.message) // 提示错误信息 return Promise.reject(error) } )
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/120802.html