Axios 配合 React Router 4 实现身份认证

上篇文章中,介绍了利用高阶组件,在 React Router 4 中实现了路由守卫。这样的路由守卫,是在前端路由层面上的守卫,属于页面校验机制的第一层:用户能否访问到相应的页面。
事实上,在页面校验时,还有一层需要进行处理:访问接口时的身份认证。某些时候,当用户登陆了系统,可以进入到某个受保护的页面,但在请求接口时,用户的身份令牌(比如 Token)失效了,这种情况下就不能让用户继续留在受保护的页面,而应该重定向至登录页,通过登陆获取新的有效的身份令牌。
一般来说,用户在访问每个接口时,都需要进行一次身份校验,如果用户身份认证失败(HTTP 状态码返回 401),就需要重定向至登录页。但在每次接口调用后,都进行一次判断的话非常麻烦,难以维护。如果我们使用的网络请求的库能够配置响应拦截器就好了,这样就可以对响应进行统一的拦截处理,只需要一份代码就可以处理身份校验。
Axios 中就有这样的功能:axios.interceptors.response 响应拦截器(对应的有 axios.interceptors.request 请求拦截器)。
下面是 Axios 的响应拦截器配合 React Router 4 的一个例子:

import axios from 'axios';
import { message } from 'antd'
import { ERROR } from '../config/error.config';
import createHistory from 'history/createHashHistory';

// 响应拦截
axios.interceptors.response.use((response) => {
    return response
}, (err) => {
    if(err.response.status === '401'){
        message.error(ERROR.LOGIN_EXPIRESSED);
        localStorage.removeItem('__config_center_token');
        localStorage.removeItem('__config_center_niceName');
        localStorage.removeItem('__config_center_imageUrl');
        const history = createHistory();
        setTimeout(() => {
            history.push('/login')
        },1500)
    }
    return Promise.reject(err)
})

当身份认证失败时,首先进行全局的跳转提示,然后移除 LocalStorage 中保存的原始数据,再通过 createHashHistory 的实例(我在项目中使用的是 HashHistory,如果使用的是 BrowserHistory 的话,需要引入 creatBrowserHistory)进行路由的跳转。
在使用 React Router 4 时,只有在 Route 组件内部才能通过 Props 获取到 history 实例,而在非 Route 组件内部想再使用 history 的功能的话,只有通过 createHashHistory 再创建一个了。

一点优化建议

上面的请求中,每个请求都会被拦截,如果一次只进行一个请求还好说,但如果同时请求多个接口,假如此时身份验证失败了,就会进行多次的弹窗提醒以及路由跳转,这个就很不友好,因此,对于一次性发出多个请求的情况,如果这些请求都失败了,最好只弹窗提醒一次。我的解决方案是加一个哨兵变量:

import { message } from 'antd'
import { ERROR } from '../config/error.config';
import createHistory from 'history/createHashHistory';
let cancelFlag:boolean = false;

// 响应拦截
axios.interceptors.response.use((response) => {
    return response
}, (err) => {
    if(err.response.status === '401'){
        if(cancelFlag) return Promise.reject(err);
        cancelFlag = true;
        message.error(ERROR.LOGIN_EXPIRESSED);
        localStorage.removeItem('__config_center_token');
        localStorage.removeItem('__config_center_niceName');
        localStorage.removeItem('__config_center_imageUrl');
        const history = createHistory();
        setTimeout(() => {
            history.push('/login')
            setTimeout(() => {
                cancelFlag = false;
            },1000)
        },1500)
    }
    return Promise.reject(err)
})

以上,就避免了多次弹窗提醒和路由跳转的情况。

完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,019评论 25 708
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,860评论 18 139
  • My. Love
    熊猫京京667阅读 180评论 0 0
  • 昨天和今天把郭涛的著作读完了,本来拿到这本书的时候感觉郭涛应该也是哗众取宠不会有太好看的东西呈现,开始是捏...
    栋姐阅读 252评论 0 0
  • 从今天开始做些改变,即使一生被别人评定为碌碌无为,也要活成自由潇洒的碌碌无为。 人生也没有几天,眨下眼,哎哟哟二十...
    乔西喜西否阅读 98评论 0 0