【问题标题】:React CSRF token issue on first render在第一次渲染时反应 CSRF 令牌问题
【发布时间】:2021-12-02 08:56:17
【问题描述】:

我需要有关 csrf 令牌问题的帮助。 现在在第一次渲染时,我正在发送获取 CSRF 令牌的请求,然后将其设置为默认标头,如下所示。

const { data } = await publicAxios.get('/csrf-token');
publicAxios.defaults.headers['X-CSRF-Token'] = data.csrfToken;
authAxios.defaults.headers['X-CSRF-Token'] = data.csrfToken;

问题是在第一次渲染时我也在发送另一个 POST 请求,但是所有请求都失败并出现 403 代码,因为这些请求没有“X-CSRF-Token”标头。 我该怎么做才能解决这个问题?

import axios from "axios";
import { createContext, useEffect } from "react";

const FetchContext = createContext();
const { Provider } = FetchContext;

const FetchProvider = ({ children }) => {
    const publicAxios = axios.create({ baseURL: '/api' })
    const authAxios = axios.create({ baseURL: '/api' })
    authAxios.interceptors.response.use(
        response => {
            return response
        },
        error => {
            return Promise.reject(error)
        }
    );

    useEffect(() => {
        const getCsrfToken = async () => {
            try {
                const { data } = await publicAxios.get('/csrf-token');
                publicAxios.defaults.headers['X-CSRF-Token'] = data.csrfToken;
                authAxios.defaults.headers['X-CSRF-Token'] = data.csrfToken;
            } catch (err) {
                console.log(err)
            }
        };
        getCsrfToken();
    }, [authAxios, publicAxios]);

    return (
        <Provider value={{ authAxios, publicAxios }}>
            {children}
        </Provider>
    );
};

export { FetchContext, FetchProvider }

这就是我的使用方式

import { useContext, useEffect, useState } from "react";
import { FetchContext } from "../Context/index";


const UsersList = ({ children }) => {
    const { authAxios } = useContext(FetchContext);
    const [userList, setUserList] = useState(null);

    useEffect(() => {
        async function getUsers() {
            try {
                const { data } = await authAxios.post(
                    '/users/list'
                );
                if (Array.isArray(data) && data.length > 0) {
                    setUserList(data)
                } else {
                    setUserList('No');
                }
            } catch (e) {
                console.log(e)
                setUserList('Error')
            }
        }

        getUsers()
    }, [authAxios]);
    
    return (
        <div>
            hello
        </div>
    )
}

【问题讨论】:

  • 您是在获取请求之前,并行还是严格之后发送该发布请求?
  • @apokryfos 我正在并行发送。所有其他请求都是使用通过 Provider 从上述代码导出的 authAxios 从不同组件发起的。所以我不明白如何在获得 csrf 令牌后发送所有请求。
  • 根据您的应用程序结构,这可能很简单,也可能是个大问题。由于您使用的是 react,那么也许您可以在 promise 中检索 csrf 令牌,然后使用 context 与所有组件共享。然后每个组件可以在发送任何需要它的请求之前等待令牌,但是如果您的设置允许它,您可能会找到一个更简单的解决方案,注意相同的承诺对象只解析一次
  • @apokryfos 感谢您的指出,让我试试类似的方法。
  • 还可以查看useContext 钩子,了解使用功能组件时的情况

标签: reactjs axios csrf-token


【解决方案1】:

这是最基本的用例:

import axios from "axios";
import { createContext, useEffect } from "react";

const FetchContext = createContext();
const { Provider } = FetchContext;

const FetchProvider = ({ children }) => {
    const [ publicAxios, setPublicAxios ] = useState(null)
    const [ authAxios, setAuthAxios ] = useState(null);

    useEffect(() => {
        const getCsrfToken = async () => {
          
            try {
                const { data } = await axios.get('/api/csrf-token');
                setPublicAxios(axios.create({ baseURL: '/api', headers: { 
                   'X-CSRF-Token': data
                } });
                setAuthAxios(axios.create({ baseURL: '/api', headers: { 
                   'X-CSRF-Token': data
                } })

            } catch (err) {
                console.log(err)
            }
        };
        getCsrfToken();
    }, [ ]);

    return (
        <Provider value={{ authAxios, publicAxios }}>
            {children}
        </Provider>
    );
};

export { FetchContext, FetchProvider }

你可以使用它:

const UsersList = ({ children }) => {
    const { authAxios } = useContext(FetchContext);
    const [userList, setUserList] = useState(null);
    useEffect(() => {
         async function getUsers() {
            try {
                const { data } = await authAxios.post(
                    '/users/list'
                );
                if (Array.isArray(data) && data.length > 0) {
                    setUserList(data)
                } else {
                    setUserList('No');
                }
            } catch (e) {
                console.log(e)
                setUserList('Error')
            }
        }
        if (authAxios !== null) {
            getUsers()
        }
    }, [authAxios]);
    
    return (
        <div>
            hello
        </div>
    )

在加载令牌并初始化实例之前,authAxios 将为 null。

重要的是在上下文提供者中包装所有需要使用这些实例的组件,例如

<FetchProvider>
  <App />
</FetchProvider>

这将使&lt;App /&gt;的所有子级都可以使用上下文

【讨论】:

  • 在您的示例中出现问题,当调用 getCsrfToken 函数时,它没有设置新的 Axios 实例,而是调用端点“/api”。这是错误 Promise {: Error: Request failed with status code 404 at createError (localhost:3000/static/js/vend...} [[Prototype]]: Promise [[PromiseState]]: "rejected" [[PromiseResult]]: Error: Request在 createError 处失败,状态码 404
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-27
  • 2021-04-14
  • 2011-09-28
  • 2021-09-15
  • 2018-01-19
相关资源
最近更新 更多