• 前端登录退出:处理Token问题(获取、缓存、失效处理)以及代码实现


    一、什么是Token

    Token是服务端生成的一串字符串,当用户第一次登陆成功后,服务器会生成一个token,并将其返回给客户端。
    当用户再次向服务器请求数据时,只需要携带着token请求数据即可,无需再次登陆用户名和密码
    目的
    通过token做一层数据拦截,可以减少数据库请求次数,减缓服务器压力

    二、获取token

    当用户第一次登陆成功之后,后台会返回一个token给到客户端,前端将token缓存到本地,然后每次发请求时需要在 header 里边带上 token,这个时候本地的 token 和后台数据库中的 token 进行一个验证,如果两者一致,则请求成功,否则失败。
    在这里插入图片描述

    三、Token失效处理

    既然前后端通过token交互,如果一直有效,会有安全风险,所以我们需要在客户端进行一下token的时间检查
    服务器的token一般不会设置太长,根据实际一般是1-7天,没有一个token是永久的,永久的token就相当于一串永久的密钥,是不安全的。

    token失效一般用两种情形:
    ① 主动退出:用户点击退出登录按钮后退出
    ② 被动退出:token过期,或被人“顶号”退出

    注意点

    无论什么方式退出token,在用户退出时,所需要执行的操作都是固定的
    1、清理掉当前用户的缓存数据
    2、清理掉相关权限的配置
    3、返回到登录页面

    1、主动退出

    用户主动点击退出功能实现
    user.js

    // 获取退出登录
    logout(context) {
      context.commit("removeToken"); // 删除token
      context.commit("reomveUserInfo"); // 删除用户信息
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、Token过期

    ① 逻辑图

    在这里插入图片描述

    ②方案

    在用户登录时,记录当前的登录时间
    制定一个token时效时长
    在接口调用时,根据当前时间对比登录时间,看是否超过了时效时间
    如果未超过,则正常进行后续操作,如果超过,则进行退出登录操作

    ③代码实现

    user.js

    // 获取登录信息
    async login(context, data) {
      const result = await loginInfo(data);
      context.commit("setToken", result);
      setTimeStamp(); // 写入时间戳
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    auth.js

    import Cookies from "js-cookie";
    const timeKey = 'liqi6limi-timestamp-key' // 设置一个独一无二的key
    
    // 获取时间戳
    export function getTimeStamp() {
      return Cookies.get(timeKey)
    }
    // 设置时间戳
    export function setTimeStamp() {
      Cookies.set(timeKey, Date.now())
    }
    
    // 设置超时时间
    export const TimeOut = 3600; 
    
    // 是否超时
    export function IsCheckTimeOut() {
      var currentTime = Date.now(); // 当前时间戳
      var timeStamp = getTimeStamp(); // 缓存时间戳
      return (currentTime - timeStamp) / 1000 > TimeOut;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    request.js

    import store from "@/store";
    import axios from "axios";
    import { getTimeStamp,IsCheckTimeOut } from "@/utils/auth";
    import router from "@/router";
    
    // 创建axios实例
    const service = axios.create({
      baseURL: "/api",
      timeout: 5000,
    });
    
    // 请求拦截器
    service.interceptors.request.use(
      (config) => {
        // 是否存在token
        if (store.getters.token) {
          if (IsCheckTimeOut()) {
            store.dispatch("user/logout"); // 退出登录的action
            router.push("/login"); // 跳转到登录页
            return Promise.reject(new Error("token超时了")); // 抛出的错误,会在响应拦截器的错误捕捉中捕捉到
            console.log("超时");
          }
          config.headers["Authorization"] = `Bearer ${store.getters.token}`; // 如果token存在 注入token
        }
        return config; // 必须返回配置
      },
      (error) => {
        return Promise.reject(error);
      }
    );
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    3、被人顶号

    ① 逻辑图

    在这里插入图片描述

    ② 方案

    后端返回数据时,会返回特定的状态码通知前端
    当前端接收到特定状态码时,表示遇到了特定状态:此时进行退出登录处理

    ③代码实现

    // 响应拦截器
    service.interceptors.response.use(
      (response) => {
        // axios默认加了一层data
        const { success, message, data } = response.data;
        //   要根据success的成功与否决定下面的操作
        if (success) {
          return data;
        } else {
          // 业务已经错误了 
          Message.error(message); // 提示错误消息
          return Promise.reject(new Error(message));
        }
      },
      (error) => {
        // error 信息 里面 response的对象
        if (
          error.response &&
          error.response.data &&
          error.response.data.code === 10002
        ) {
          // 当等于10002的时候 表示 后端告诉我token超时了
          store.dispatch("user/logout"); // 登出action 删除token
          router.push("/login");
        } else {
          Message.error(error.message); // 提示错误信息
        }
        return Promise.reject(error);
      }
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
  • 相关阅读:
    Linux系统之时间同步方法
    外包干了3个月,技术倒退2年。。。
    在Python中匿名函数怎么用?
    【猿创征文】Vue3 企业级优雅实战 - 组件库框架 - 6 搭建example环境
    uniapp检测新版本
    柔性数组笔记
    python爬虫实战——小说爬取
    Revive开发商加入VR开源标准OpenXR
    阿里飞猪电话面一个小时9分钟
    【Swift 60秒】55 - Shorthand parameter names
  • 原文地址:https://blog.csdn.net/Vest_er/article/details/127696264