将 axios 与 useeffect 一起使用时,在 JavaScript 代码中获取争用条件

Getting race condition in JavaScript code when using axios with useeffect

提问人:firstuser 提问时间:11/8/2023 更新时间:11/8/2023 访问量:81

问:

我在 react 中使用 csrf 令牌和 jwt 身份验证,我的 jwt 令牌存储在 httponly cookie 中,所以我的前端无法访问此 cookie,所以我在我的 spring 后端中制作了一个端点,用于检查用户是否经过身份验证并返回 true 或 false。也是将 csrf-token 作为 cookie(non-httponly cookie)获取的端点,因此获取的 csrf-cookie iam 的名称是,它的相应值是 csrf 令牌值。因此,根据 Spring Security,我应该将此 CSRF 令牌作为标头发送,其中包含 CSRF 令牌的名称和值,并且对于每个状态(指后端)更改请求(如 etc)删除 CSRF 令牌,并且不会更改请求,因此对于每个状态(指后端)更改请求,我们应该通过调用端点来生成一个新令牌,因此,而不是每次创建时都重复所有这些步骤我的 javascript 中的 HttpUtility 类 获取 csrf 令牌并检查我的请求是否经过身份验证 HttpUtil 类的代码是:api/user/is-authenticatedapi/user/csrf-tokenXSRF-TOKENX-XSRF-TOKENPOST,PUT,DELETEGETapi/user/csrf-token

import axios from 'axios';
import Cookies from 'js-cookie';

class HttpRequestUtility {

    static axiosInstance = axios.create({
        withCredentials: true,
        headers: {
            'Content-Type': 'application/json'
        }
    });

    static csrfAxiosInstance = axios.create({
        withCredentials: true
    })

    static authenticatedAxiosInstance = axios.create({
        withCredentials : true,
        headers: {
            'Content-Type': 'application/json'
        }
    })

    static axiosInstanceConfigured = false;

    static authenticatedAxiosInstanceConfigured = false;

    static configAxiosInstance() {

        // Add a request interceptor
        this.axiosInstance.interceptors.request.use(async config => {
            // Call your function to get the CSRF token
            const csrfToken = await this.getCsrfToken();

            // Set the CSRF token in the headers
            config.headers['X-XSRF-TOKEN'] = csrfToken;

            return config;
        }, error => {
            return Promise.reject(error);
        });

    }

    static configAuthenticatedAxiosInstance() {

        // Add a request interceptor
        this.authenticatedAxiosInstance.interceptors.request.use(async config => {

            const isAuthenticatedUrl = "http://localhost:8080/api/user/is-authenticated"
            
            if(await this.getAxiosInstance().get(isAuthenticatedUrl)){
            // Call your function to get the CSRF token
            const csrfToken = await this.getCsrfToken();

            // Set the CSRF token in the headers
            config.headers['X-XSRF-TOKEN'] = csrfToken;


                return config;
            }
            else{
                throw new Error("User is not Authenticated")
            }

        }, error => {
            return Promise.reject(error);
        });

    }


    static async getCsrfToken() {
        let csrfCookie = Cookies.get('XSRF-TOKEN');
        if (!csrfCookie) {
            try {
                await this.csrfAxiosInstance.get('http://localhost:8080/api/user/csrf-token');
                csrfCookie = Cookies.get('XSRF-TOKEN');
            } catch (error) {
                console.error("Failed to get CSRF token ", error);
            }
        }
        return csrfCookie
    }


    static getAxiosInstance() {
        if (!this.axiosInstanceConfigured) {
            this.configAxiosInstance();
            this.axiosInstanceConfigured = !this.axiosInstanceConfigured
        }
        return this.axiosInstance;
    }

    static getAuthenticatedAxiosInstance(){
        if(!this.authenticatedAxiosInstanceConfigured){
            this.configAuthenticatedAxiosInstance();
            this.authenticatedAxiosInstanceConfigured = !this.authenticatedAxiosInstanceConfigured
        }
        return this.authenticatedAxiosInstance;
    }

}

export default HttpRequestUtility

我在 react 中有一个 useEffect 钩子,如下面的代码所示,它有一个 get 请求,它不会更改状态(指后端),因此它不会删除 csrf 令牌。而且我的useEffect钩子中也有一个请求,它改变了状态(指的是后端),所以它删除了前端中的csrf-token,我的代码是这样的:PUT

useEffect(() => {
  const fetchData = async () => {
    try {
      const getUserUrl = "http://localhost:8080/api/user/get";
      const response = await HttpRequestUtility.getAuthenticatedAxiosInstance().get(getUserUrl);
      response.data.noteIds.map((element)=>{
        console.log("Note id's are : "+element)
      })
      console.log("users are ", response.data);

      const data = {
        username: "test Data"
      }

      await HttpRequestUtility.getAuthenticatedAxiosInstance().put("http://localhost:8080/api/user/edit",data)

    } catch (error) {
      console.log(error);
      const logoutUrl = "http://localhost:8080/api/user/logout"
      await HttpRequestUtility.getAxiosInstance().post(logoutUrl)
      navigate("/login");
    }
  }

  fetchData();

}, [])

注意:上面的useEffect代码用于演示我的问题,我不会像那样使用它来展示我的问题,我正在举例说明用例

所以当这段代码从我的 HttpUtility 类中执行时:

    static async getCsrfToken() {
        let csrfCookie = Cookies.get('XSRF-TOKEN');
        if (!csrfCookie) {
            try {
                await this.csrfAxiosInstance.get('http://localhost:8080/api/user/csrf-token');
                csrfCookie = Cookies.get('XSRF-TOKEN');
            } catch (error) {
                console.error("Failed to get CSRF token ", error);
            }
        }
        return csrfCookie
    }

由于 useEffect 钩子在 react 开发模式下执行两次,它正在向端点发送两个 put 请求,我的问题不在于 useeffect 如何发送两个 put 请求,我的问题是当我在执行多个状态更改请求时,例如从前端一个接一个地使用 HttpUtility 类作为 javascript 是单线程和事件驱动的语言,它正在上述方法中进行上下文切换它从 useEffect 到达第一个 put 请求,因为它是网络调用请求,它正在执行第二个 put 请求,当第二个 put 请求执行到此代码时,它会转到第一个 put 请求并删除“XSRF-TOKEN”cookie,因为它是一个状态(指后端)更改请求,所以当第二个 put 请求恢复并转到这一行时,我未定义为csrf 令牌已删除,请求在没有 csrf 令牌的情况下执行,我收到错误。所以任何人都可以帮助我更好地实现 httpUtility 类,这样它就不会进入这种竞争条件。api/user/editPUT,POST,DELETEgetCsrfTokenawait this.csrfAxiosInstance.get('http://localhost:8080/api/user/csrf-token');await this.csrfAxiosInstance.get('http://localhost:8080/api/user/csrf-token');csrfCookie = Cookies.get('XSRF-TOKEN')

我还为我的方法实现了一个锁定变量,如下所示:getCsrfToken

static lock = Promise.resolve();

static async getCsrfToken() {
    let csrfCookie = Cookies.get('XSRF-TOKEN');
    if (!csrfCookie) {
        // Wait for the lock to be released before proceeding
        await lock;

        lock = (async () => {
            try {
                await this.csrfAxiosInstance.get('http://localhost:8080/api/user/csrf-token');
                csrfCookie = Cookies.get('XSRF-TOKEN');
            } catch (error) {
                console.error("Failed to get CSRF token ", error);
            }
            return csrfCookie;
        })();
        
        // Wait for the lock to be released before returning
        csrfCookie = await lock;
    }
    return csrfCookie;
}

在此版本的函数中,我们在锁中使用异步函数。这允许我们将 await 与 get 请求一起使用。设置锁后,我们等待锁,然后再返回 csrfCookie。这可确保在发布请求和 cookie 检索完成之前,函数不会返回。

但这也不起作用,任何人都可以帮助我,我从昨天开始就陷入了这个问题

javascript java reactjs spring-security

评论


答: 暂无答案