This page looks best with JavaScript enabled

vue token过期无缝刷新

 ·  ☕ 3 min read  ·  🎅 [emacle] · 👀... views

过期刷新流程图:

思路:

  1. 登录时, 后端生成 access_token, refresh_token 返回前端, 前端保存两个token在 cookie或localstorge中

  2. 当前端发送正常请求时,请求头字段携带 access_token , 后端提取该 access_token

    • 判断是否过期, 不过期则返回 HTTP 200 OK
    • 过期返回 HTTP_UNAUTHORIZED 401, 并且加上自定义响应数据 code = 50014 表示access_token 过期
  3. VUE前端使用 响应拦截器 , 对收到的 HTTP 401 进行拦截, 如果 http 401 且 code =50014 则先以 refresh_token
    去获取新 access_token

    • 如果正常获得 access_token, 则再次以新 access_token 发送原请求, 即可实现无缝刷新
    • 如果 refresh_token 也过期, 则服务器也返回 401, 但是加上了自定义响应数据 code= 50015, 前端的响应拦截器
      再次捕获到 error , 校验code =50015后, 则强制退出需要重新登录
     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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    
    // response interceptor
    service.interceptors.response.use(
        response => {
       const res = response.data
       // 一些处理...
       return response.data
        },
        error => {
       // http 401 只能在 error 里被截获
       // console.log(error) *** 控制台不能输出返回的响应数据 ***
       // console.log(error.response) *** 可使用此命令进行调试 ***
       if (error.response.status === 401 && error.response.data.code === 50014) {
           // message: 'access_token过期,自动续期', code = 50014 access_token 过期
           return againRequest(error) // 此函数先以refresh_token 去获取新access_token, 然后再次以新 access_token 发送原请求
       }
    
       // 这里是 code = 50015 refresh_token 也过期的情况
       if (error.response.status === 401 && error.response.data.code === 50015) {
           // message: 'refresh_token过期,重定向登录', code = 50015 refresh_token 过期
           console.log('refresh_token过期 超时......')
           MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
               confirmButtonText: '重新登录',
               cancelButtonText: '取消',
               type: 'warning'
           }).then(() => {
               store.dispatch('FedLogOut').then(() => {
                   location.reload() // 为了重新实例化vue-router对象 避免bug
               })
           })
       }
    
       return Promise.reject(error)
        }
    ) // response 拦截结束
    
    async function againRequest(error) {
        await store.dispatch('handleCheckRefreshToken') // 同步以获取刷新 access_token 并且保存在 cookie/localstorage
        const config = error.response.config
        config.headers['X-Token'] = getToken()  // 以新的 access_token
        const res = await axios.request(config) // 重新进行原请求
        return res.data // 以error.response.config重新请求返回的数据包是在函数内是 被封装在data里面
    }
    
  4. 第3步以 refresh_token 去获取 access_token 时, 必须在 请求拦截器 里重新配置请求头, 以 refresh_token 作为新的 token 头
    否则后端token认证判断还是原过期的 access_token

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    // request interceptor
    service.interceptors.request.use(
        config => {
       // Do something before request is sent
       if (store.getters.token) {
           // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
           config.headers['X-Token'] = getToken()
       }
    
       // 监听是否 /sys/user/refreshtoken 是则重置token为 refresh_token
       const url = config.url
       if (url.split('/').pop() === 'refreshtoken') {
           // console.log('config.url', config.url, getRefreshToken())
           config.headers['X-Token'] = getRefreshToken() // 登录时本地 cookie/localstorage 存储的
       }
       return config
        },
        error => {
       // Do something with request error
       console.log(error) // for debug
       Promise.reject(error)
        }
    )
    



    1
    
    用户编辑时比较有用, 防止长时间编辑后提交时 access_token 过期, 导致编辑内容丢失
    
  5. 完整代码 https://github.com/emacle/vue-php-admin

  6. VUE 请求拦截器与响应拦截器代码修改自 vue-element-admin 中的 request.js

参考: php firebase/php-jwt token验证


emacle
WRITTEN BY
[emacle]
Emacser orgmode

 

What's on this Page