zoukankan      html  css  js  c++  java
  • 基于OAuth2.0的token无感知刷新

      目前手头的vue项目关于权限一块有一个需求,其实架构师很早就要求我做了,但是由于这个紧急程度不是很高,最近临近项目上线,我才想起,于是赶紧补上这个功能。这个项目是基于OAuth2.0认证,需要在每个请求的头部携带access_token,如果这个access_token过期,需要利用已有的refresh _token去重新获取一个access_token,如果连这个refresh_token也过期了,那就是真正的过期了,需要退出登录页面。refresh_token在获取新的access_token的时候需要让用户无感知,也叫无痛刷新。

      这里的代码实现肯定是要在axios拦截器里写的,但是是在请求拦截器里写还是在响应拦截器里写还是有区别的:

      1.写在请求拦截器里:每次请求之前都会先请求一个checkToken的接口,来确认这个access_token是否过期,如果没有过期,直接就发起原本的请求,如果过期,利用已有的refresh _token去重新获取一个access_token之后,再发起原本的请求。但是这样写有个缺点,就是每次请求之前都要额外请求一次checkToken的接口,如果网速不好,会给用户造成不好的体验,而且对服务器造成了性能上的浪费。

      2.写在响应拦截器里:直到access_token过期,返回401未授权,才利用已有的refresh _token去重新获取一个access_token。

      最后我和后端讨论了下,最后采用了第二种方法,把checkToken放在后端,前端无感知刷新写在响应拦截器里。

    这里写的一个响应拦截器:

    import axios from 'axios'
    
    //创建一个axios实例
    const service = axios.create({
      timeout: 5000, // 请求超时时间
      withCredentials:true //表示跨域请求时是否需要使用凭证. 默认为false
    })
    var loading;//遮罩层
    
    // 响应拦截器
    service.interceptors.response.use( response => { //do what you like }, error => { loading.close(); if (error && error.response) { switch (error.response.status) { case 400: error.message = '请求错误' break case 401: return doRequest(error); case 403: error.message = '拒绝访问' break case 404: error.message = `请求地址出错: ${error.response.config.url}` break case 408: error.message = '请求超时' break case 500: error.message = '服务器内部错误' break case 501: error.message = '服务未实现' break case 502: error.message = '网关错误' break case 503: error.message = '服务不可用' break case 504: error.message = '网关超时' break case 505: error.message = 'HTTP版本不受支持' break default: break } } errorLog(error) return Promise.reject(error) } )

      export default service
     

      可以看到在响应拦截器的错误回调函数里401值的时候调用了一个方法doRequest();

    async function doRequest (error) {
      try {
        const data = await getNewToken();
        var token=data.data.token_type+' '+data.data.access_token;
        sessionStorage.setItem('RequestToken',token);
        const res = await service.request(error.config)
        return res;
      } catch(err) {
        Message({
          message: '登录会话已过期,请重新登录',
          type: 'error',
          duration: 5 * 1000
        })
        sessionStorage.clear();
        router.replace({
          path:"/login"
        });
        return err;
      }
    }

      这里的重点这些请求必须是同步的,同步的,同步的,重要的事情说三遍,而axios默认是异步的,所以你要么使用ES6的async/await语句,要么使用then回调函数,必须保持是同步的。而getNewToken()则是利用refresh_token重新获取access_token方法。算了,一并贴出,仅作参考。

    import qs from 'qs'
    
    async function getNewToken() {
      var refreshToken=sessionStorage.getItem('refreshToken');
      return await axios({
        url: '/OAuth/oauth/token',
          method: 'post',
          headers: {
            'Authorization': 'Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
            'Content-Type':'application/x-www-form-urlencoded'
          },
        data:qs.stringify({
          grant_type:'refresh_token',
          refresh_token : refreshToken
        })
      })
    }

      下面看效果。为了效果,这里设置了access_token有效时间为5s,refresh _token有效时间为10s。动图是这样的:

      一步一步分解下,登录的时候,获取到access_token和refresh _token。然后带着access_token:f0a3******cb64去访问menuQuery接口是可以正常请求的。

       但是之后,我等了超过5s后(不超过10s,这个时候access_token已过期,refresh _token未过期)发了一个对0304接口的请求,这个时候返回401未授权,说明access_token:f0a3******cb64已过期。

       这时利用refresh_token重新获取access_token。

      可以看到返回了一个新的access_token:8332******1c8a,于是带着这个新的access_token重新发起对0304接口的请求,这个时候就可以返回所需要的数据。

     

      之后再等超过5s,这个时候access_token过期了,refresh _token也过期了。动图是这样的:

       这时的请求返回的是400,而不是401了,这说明refresh _token:826b******17d1过期了。这个时候就该退出登录界面,重新登录了。

       最后,放一个总的效果图:

  • 相关阅读:
    IOS面试题
    iOS开发多线程篇—线程的状态
    iOS开发多线程篇—GCD介绍
    iOS开发多线程篇—线程间的通信
    iOS开发多线程篇—线程安全
    iOS开发多线程篇—创建线程
    iOS开发多线程篇—多线程简单介绍
    iOS开发UI篇—Quartz2D使用(绘制基本图形)
    iOS开发UI篇—Quartz2D(自定义UIImageView控件)
    iOS开发UI篇—核心动画(UIView封装动画)
  • 原文地址:https://www.cnblogs.com/jdWu-d/p/12643822.html
Copyright © 2011-2022 走看看