axios 后台管理项目 - token刷新,拦截器设置

最近在公司做一个vue.js的后台管理系统。鉴于自己也是vue小白,在网上查阅了很多现成的资料,总算是把需求搞定了。
先来介绍一下需求:登录时请求token和refresh_token(用来刷新token),每一次请求后端接口的时候都要在header里带token,如果过期会报错401,在过期的半小时内用refresh_token重新请求可以刷新token,使用户不必被强制退出重新登录。

这个项目使用的axios和vue-router,解决方法如下:

参考:http://blog.csdn.net/qq673318522/article/details/55506650

思路

axios设置的思路是,先设置默认的数据,比如说超时时间,url前缀,然后设置request和response拦截器,request拦截器是在发送请求之前执行,response拦截器是在请求返回之后执行。
这里实现刷新token主要是在response拦截器做文章,请求返回401时先记录这次请求的设置config,然后请求刷新接口,刷新成功之后再重新发送一次之前失败的请求,因为config保存下来,所以发送请求的时候可以先把config中的token换成最新的token,再直接用axios(config),而不用担心其他的设置,从而实现重新请求。
那么问题在于如何知道刷新token成不成功呢,如果无脑反复请求会变成死循环,因此需要设置一个变量在config中,请求过刷新接口之后设置这个变量为true,这样就不会重复刷新了。

代码

http.js内容如下:

import axios from 'axios'
import {Loading, Message} from 'element-ui'
import qs from 'qs';
import store from '../store/index'
import router from '../router/index'

// axios 配置
axios.defaults.timeout = 5000
axios.defaults.baseURL = 'api/'
//用来处理刷新token后重新请求的自定义变量
axios.defaults.isRetryRequest = false

//刷新token的请求方法
function getRefreshToken() {
    //refresh_token使用vuex存在本地的localstorage,之后会详细说
    let params = {
        grant_type: 'refresh_token',
        refresh_token: store.state.currentUser.UserRefreshToken
    };
    //qs的使用主要是因为该接口需要表单提交的方式传数据,具体使用方法自行百度
    return axios.post('oauth/token', qs.stringify(params));
}

// http request 拦截器
var loadinginstace;
axios.interceptors.request.use(
    config => {
        //获取储存在本地的token值
        let authToken = store.state.currentUser.UserToken;
         //这边可根据自己的需求设置headers,我司采用basic基本认证
        if (authToken === null) {
            authToken = window.btoa("nucleus" + ":" + "nucleus-secret");
            config.headers.Authorization = `Basic ` + authToken;
            config.headers['Content-Type'] = 'application/x-www-form-urlencoded';
        }
        else {
            config.headers.Authorization = `Bearer ` + authToken
        }

        //这是element-ui的效果,全页面遮罩,中间带有加载圈
        loadinginstace = Loading.service({fullscreen: true})
        return config
    },
    err => {
        //这边是参考上面的链接的,具体有什么用我目前还没测到,反正加载超时不是在这边显示
        loadinginstace.close()
        Message.error({
            message: '加载超时'
        })
        return Promise.reject(err)
    }
);

// http response 拦截器
axios.interceptors.response.use(
    response => {
        //关闭遮罩层,非常重要,不然页面都不能操作了!
        loadinginstace.close();
        return response
    },
    err => {
        if (err.response) {
            switch (err.response.status) {
                case 401:
                    let config = err.config;
                    /*用vuex删除token
                    *因为刷新token的接口和登录接口一样
                    *用basic认证和表单提交的方式
                    *需要区别于普通接口调用*/
                    store.dispatch('DelToken');
                    //判断是否已经刷新过token
                    if (!config.isRetryRequest) {
                        return getRefreshToken()
                            .then(function (res) {
                                let data = res.data;
                                //用vuex重新设置基本信息
                                store.dispatch('UserLogin', {
                                    username: store.state.currentUser.UserName,
                                    token: data.access_token,
                                    refresh_token: data.refresh_token
                                });
                                //修改flag
                                config.isRetryRequest = true;
                                //修改原请求的token
                                let authToken = store.state.currentUser.UserToken;
                                config.headers.Authorization = `Bearer ` + authToken;
                                /*这边不需要baseURL是因为会重新请求url
                                *url中已经包含baseURL的部分了
                                *如果不修改成空字符串,会变成'api/api/xxxx'的情况*/
                                config.baseURL = '';
                                //重新请求
                                return axios(config);
                            })
                            .catch(function () {
                            //刷新token失败只能跳转到登录页重新登录
                                store.dispatch('UserLogout');
                                router.replace({
                                    path: 'login',
                                    query: {redirect: router.currentRoute.fullPath}
                                });
                                throw err;
                            });
                    }
                    break;
            }
        }
        else{
            Message.error({
                message: '加载超时'
            })
        }
        loadinginstace.close();
        return Promise.reject(err)
    }
);

export default axios

你可能感兴趣的:(前端学习)