【前端开发方案】后台管理系统之登录方案、退出登录方案记录

5 篇文章 0 订阅

登录方案记录

需求:根据当前环境的不同,请求不同的 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
  })
}

将封装登录请求处理封装到 vuexaction 中。在 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'

vuexuser 模块下,处理 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')
}

为退出登录按钮添加点击事件,触发 logoutaction

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.jslogin 中:

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)
  }
)

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来自湖南的阿晨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值