Vue + Refresh Token

一、关于 Refresh Token

之前公司项目中使用了 Refresh Token ,作为前端理解 Refresh Token 花了一些功夫。

为什么在已有 Access Token 的基础上还要再加一层 Refresh Token ?因为 Access Token 本身有过期时间,很多网站长时间不登录都会提示 Access Token 过期重新登录,然后某些记性不好的小伙伴就开始了忘记密码找回的流程,用户体验不是很好,而如果设置过长的 Access Token 过期时间那么安全性又会降低,Refresh Token 就解决了这两个矛盾的问题。

Refresh Token 由于仅仅用来请求普通 Token ,对安全性的要求没那么强烈,因此可以设置较长的过期时间,而当 Token 面临过期时,使用 Refresh Token 来重新获取 Access Token ,然后再用 Access Token 继续之前未完成的操作。

二、Vue 实现

后端需要专门的接口用来进行 Refresh Token 对 Access Token 的请求。

在 axios 的 http 拦截器里加入对 Refresh Token 和 Access Token 的相关处理。

1、获取 Token

export const getToken = () => {
  const tokenObj: TokenObj = {};
    // 从本地存储中获取 Refresh Token 和 Access Token 及它们的过期时间
  tokenObj.accessToken = localStorage.getItem('accessToken');
  tokenObj.accessTokenExpiresTime = localStorage.getItem('accessTokenExpiresTime');
  tokenObj.refreshToken = localStorage.getItem('refreshToken');
  tokenObj.refreshTokenExpiresTime = localStorage.getItem('refreshTokenExpiresTime');
  return tokenObj;
};

2、存储 Token

// 使用 axios 创建 http
export const http = axios.create({
  baseURL: process.env.VUE_APP_API_URL,
  withCredentials: false, // 跨域请求时发送 cookies
  timeout: 30000
});

export const setToken = (tokenObj) => {
  // 将 Access Token 添加进 http 默认请求头
  http.defaults.headers['Authorization'] = `${tokenObj.accessToken}`;
  Object.keys(tokenObj).forEach(key => {
      if (tokenObj[key]) {
          localStorage.setItem(key, tokenObj[key]);
      }
  });
};

2、判断接口是否是 Refresh Token 专用接口

export const setConfigHeaders = (tokenObj: TokenObj, config: AxiosRequestConfig) => {
  if (tokenObj.accessToken && tokenObj.refreshToken && config.url) {
    if (config.url.indexOf('/refresh-token') >= 0) {
      // 此处为 Refresh Token 专用接口,请求头使用 Refresh Token
      config.headers['Authorization'] = `${tokenObj.refreshToken}`;
    } else if (!(config.url.indexOf('/login') !== -1 && config.method === 'post')) {
      // 其他接口,请求头使用 Access Token
      config.headers['Authorization'] = `${tokenObj.accessToken}`;
    }
  }
};

3、请求拦截器处理

// 全局刷新状态,防止重复请求 /refresh-token 接口
window.isRefeshing = false;
// 原始请求队列
let requests: any = [];

export function requestHandler(
    config: AxiosRequestConfig
): AxiosRequestConfig | Promise<AxiosRequestConfig> {
  // 获取本地存储的 Token
  const tokenObj = getToken();
  // 将 Token 塞入请求头
  setConfigHeaders(tokenObj, config);

  if (config.url) {
    config.url = `/api/v1${config.url}`; // Restful Api 版本内容

    // 如果是 Refresh Token 专用接口或登录接口,不需要进一步处理
    if (config.url.indexOf('/refresh-token') >= 0 || config.url.indexOf('/login') >= 0) {
      return config;
    }
  }
  
  // 如果本地存储的 Token 相关内容无误,继续下一步处理
  if (tokenObj.accessToken && tokenObj.accessTokenExpiresTime && tokenObj.refreshToken && tokenObj.refreshTokenExpiresTime) {
    // 获取当前时间
    const now = Date.now();
    // 判断 Refresh Token 是否过期,要是它都过期了,那就要重新认证了
    if (now > tokenObj.refreshTokenExpiresTime) {
      clearToken();
      return config;
    }
    // Access Token 过期
    if (now > tokenObj.accessTokenExpiresTime) {
      if (!window.isRefeshing) {
        // 开始处理
        window.isRefeshing = true;
        // 请求 /refresh-token 接口
        authRefreshToken().then(data => {
          // 获取到全新的 Token 信息
          const {accessToken, accessTokenExpiresIn, refreshToken, refreshTokenExpiresIn} = data;
          // 计算出新的过期时间
          const accessTokenExpiresTime = now + accessTokenExpiresIn;
          const refreshTokenExpiresTime = now + refreshTokenExpiresIn;
          // 调用上文的 setToken 方法
          setToken({
            accessToken, accessTokenExpiresTime, refreshToken, refreshTokenExpiresTime
          });
          window.isRefeshing = false;
          return accessToken;
        }).then(token => {
          requests.forEach((cb: any) => cb(token));
          // 执行完成后,清空队列
          requests = [];
        }).catch(() => {
          // 错误则清除所有 Token 相关信息,让用户重新认证
          window.isRefeshing = false;
          clearToken();
          return config;
        });
      }

      // 原有的请求队列,在重新获取完 Access Token 后继续执行之前未完成的请求
      // 适用于同时多个请求的情况
      return new Promise(resolve => {
        requests.push((accessToken: string) => {
          config.headers['Authorization'] = `${accessToken}`;
          resolve(config);
        });
      });
    }
  }
  return config;
}

点此访问我的个人博客

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue是一种流行的JavaScript框架,用于构建单页面应用程序(SPA)。Spring Boot是一种Java框架,用于构建Web应用程序。 Spring Security是Spring框架的安全性模块,用于提供身份验证和授权功能。WebSocket是一种协议,用于在Web应用程序中实现双向通信。Token则是在用户验证过程中传输的一种凭证。 Vue和Spring Boot的流行度,使得它们是构建现代Web应用程序的理想选择。当应用程序需要安全性和实时性时,Spring Security和WebSocket技术是最佳的选择。在Spring Boot中,使用Spring Security模块可以轻松地添加身份验证,并对请求进行基于角色的访问控制。此外,Spring Security还提供了多种身份验证方案,如基础身份验证和JWT身份验证。 对于实时性,WebSocket提供了一种优雅的解决方案。在Web应用程序中,传统的HTTP请求是一种单向通信模式,即客户端发起请求,服务器响应请求。WebSocket协议通过建立持久连接,允许双向通信。这意味着服务器可以在没有客户端请求的情况下向客户端发送数据,从而实现实时更新。这尤其适用于聊天和数据可视化应用程序。 当使用Vue和Spring Boot构建实时应用时,可以使用WebSocket和Vuevue-socket.io插件轻松地实现数据传输。Vue-socket.io插件允许将socket.io集成到Vue组件中,以便在应用程序中使用。 当应用程序需要安全性时,可以使用Spring Security的JWT身份验证方案。这需要在服务器端创建一个JWT令牌,并将其发送到客户端。客户端在发送后每次请求时都要将这个令牌包含在请求中。服务器将验证这个令牌,并使用已经验证的用户身份对请求进行授权。 总之,Vue,Spring Boot,Spring Security和WebSocket的结合可以为Web应用程序的安全性和实时性提供完美的解决方案。使用JWT身份验证和Vue-socket.io插件,可以轻松地实现这些功能。无论是聊天应用程序还是数据可视化应用程序,这些技术都可以提高应用程序的用户体验。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值