React中利用axios进行Jwt登录认证

后台我们采用的是ASP.NET Core Web API,请参见:ASP.NET Core Web API 依赖注入及JWT认证配置

React采用的版本是17.0.2,UI采用的是Ant Design和少量Ant Pro组件。这篇文章我们只写有关Jwt认证的部分。

网络请求库我们采用的是axios。我们稍微封装了一下:

import axios from 'axios'
import { message } from "antd";
import eventBus from './eventBus' 
let messageFlag = false;
let navigate: any = "";

// 也可以不使用事件机制,采用其它方式也可以
eventBus.$on((propsObj: any) => {
    navigate = propsObj;
}, 'axiosInterceptorsFun');

const goLoginFun = () => {
    if (messageFlag === false) {
        message.warning("登录已过期,请重新登录!")
        messageFlag = true;
        setTimeout(() => {
            messageFlag = false;
        }, 2000);

        //navigate是在Layout组件中传递过来的
        navigate && navigate('/login');
    }
}

const goNoPermission = () => {
    navigate && navigate('/nopermission');
}

// 创建axios新实例;如果直接使用axios会导致拦截器在页面刷新以后丢失配置
const axiosInstance = axios.create({ timeout: 60000, withCredentials: true });

axiosInstance.interceptors.request.use(function (request: any) {
    const localToken = localStorage.getItem('jwt-token');

    if (localToken) {
        request.headers['Authorization'] = "Bearer  " + localToken;
    }
    return request;
});

axiosInstance.interceptors.response.use(function (response) {
    // 成功时按默认方式处理
    // let {status, data: { code } } = response;    
    // status在200和300(不含300)之间执行then,否则执行catch
    return response;
},
    function (error) {
        if (error.response) {
            if (error.response.status === 401) {
                // 未登录时,后台返回401
                goLoginFun();
            }
            else if (error.response.status === 403) {
                // 已登录,权限不足时,后台返回403
                goNoPermission();
            }

            return Promise.reject(error.response);
        }
        else {
            return Promise.reject(error.message);
        }
    });

// 登录成功后调用此方法将jwt存入localStorage
export const SaveJwtToLocal = (jwt: string) => {
    localStorage.setItem('jwt-token', jwt);
}

// 退出登登录时,调用此方法即可
export const Logout = () => {
    localStorage.removeItem('jwt-token');
    navigate && navigate('/login');
}

// 用到了ProTable组件,此处忽略
// 将ProTable的params,sort扁平化
export const ParsePagingParams = (params: any, sort: any) => {
    const result = { ...params };
    if (sort) {
        const keys = Object.keys(sort);
        if (keys.length > 0) {
            result["sortField"] = keys[0];
            result["sordDirection"] = sort[keys[0]];
        }
    }
    return result;
}

// 常规的json请求,调用此方法
export const PostJson = (api: string, data: any = {}) => {
    return new Promise((resolve, reject) => {        
        axiosInstance.post("/api/" + api, data).then(res => {
            resolve(res);
        }, res => {
            reject(res);
        });
    });
}

export const Get = (api: string) => {
    return new Promise((resolve, reject) => {
        axiosInstance.get("/api/" + api).then(res => {
            resolve(res);
        }, res => {
            reject(res);
        });
    });
}

其中用到了一个eventbus(懒得写,直接网上复制的;其实也可以不用eventbus,react提供了其它方式也可以实现),代码: 

interface eventBusType {
    evnetList: Array<any>;
    $on: (callbackFun:any, name:string)=>void;
    $emit: (sname:string, data:any)=>void;
    $remove: (name:string)=>void;    
}

const eventBus:eventBusType = {
    evnetList: [],   
    $on(callbackFun, name) {        
        if (this.evnetList.length > 0 && this.evnetList.find((i:any) => i.name === name)) {
            this.evnetList = this.evnetList.filter((i:any) => i.name !== name)
        }
        this.evnetList = [...this.evnetList, { name, callbackFun }]
    },    
    $emit(name, data= '') {
        if (!name) {
            return false;
        }
        this.evnetList.forEach((element:any) => {
            if (name === element.name) {
                element.callbackFun(data)
            }
        });
    },    
    $remove(name = "") {
        this.evnetList = [...this.evnetList.filter((i:any) => i.name !== name)]
    }
}
export default eventBus 

另外,layout组件中,触发事件代码:

  const navigate = useNavigate();
  eventBus.$emit("axiosInterceptorsFun", navigate);

登录页面的主要代码:

  const onfinish = async (values: any) => {

    await PostJson('authorize/login',{loginId:values.userName,passWord:values.password})
      .then((data: any) => {

        if (data.data.code === 0 && data.data.data) {
          SaveJwtToLocal(data.data.data);
          loginStatus = true;
        }
        else if (data.data.code === 1) {
          message.error(data.data.message);
        }
      }).catch((err) => { console.log("catch:" + err) });

    //不能在PostJson的回调函数中使用navigate
    if (loginStatus === true) {
      navigate("/welcome", { replace: true });
    }

  }

这个app布局采用的是左侧导航模式,效果如图:

 React涉及的东西比较多,比如:布局,路由,状态管理等等。这些可以在实际项目开发中逐步熟悉。

React使用Axios的过程,如果要刷新Token并重新请求接口,可以采用以下两种方式: 1.使用拦截器:Axios提供了interceptors拦截器,可以在请求或响应被处理前对它们进行全局的拦截和处理。可以在请求拦截器判断Token是否过期,如果过期,则先刷新Token,然后再重新发起请求。示例代码如下: ``` axios.interceptors.request.use( (config) => { const token = localStorage.getItem("token"); if (token) { // 判断Token是否过期,如果过期则刷新Token const decodedToken = jwt_decode(token); const currentTime = Date.now() / 1000; if (decodedToken.exp < currentTime) { // 刷新Token的代码 } config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => { return Promise.reject(error); } ); ``` 2.使用Promise.all():Promise.all()方法接收一个Promise对象的数组作为参数,并返回一个新的Promise对象。当所有的Promise对象都成功时,新的Promise对象才会成功;当有一个Promise对象失败时,新的Promise对象就会失败。可以在Promise.all()方法同时发起原始请求和刷新Token的请求,当Token刷新后再重新发起原始请求。示例代码如下: ``` const refreshTokenPromise = axios.post("/api/refreshToken"); const originalPromise = axios.get("/api/someApi"); Promise.all([refreshTokenPromise, originalPromise]) .then((responses) => { const originalResponse = responses[1]; // 处理原始请求的响应数据 }) .catch((error) => { // 处理错误 }); ``` 以上是两种处理方式,你可以根据实际情况选择适合自己的方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值