You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何防止AWS Amplify Auth事件重复触发导致后端多次调用?

解决AWS Amplify Auth事件重复触发导致后端多次调用的问题

你的问题核心在于Auth事件触发速度远快于eventCancelToken()的执行时机——当多个signIn/cognitoHostedUI事件同时触发时,多个getUserAndProcess已经进入异步流程,此时取消监听已经拦不住正在执行的请求了。下面是几个可行的解决思路:

思路1:添加执行锁,避免并发调用

在组件内维护一个状态标记是否正在处理用户,只有当标记为false时才执行逻辑,从根源上阻止重复调用:

// 组件内新增状态
const [isProcessing, setIsProcessing] = useState(false);

const getUserAndProcess = async (eventCancelToken: () => void) => {
    // 如果正在处理,直接返回
    if (isProcessing) return;
    setIsProcessing(true);
    
    try {
        console.log("processing user...");
        const currentUser = await Auth.currentAuthenticatedUser() as CognitoUser;
        if(currentUser != null) eventCancelToken();
        setUser(currentUser);

        // 获取token逻辑
        const userSession = currentUser.getSignInUserSession();
        const idToken = userSession?.getIdToken().getJwtToken();
        const accessToken = userSession?.getAccessToken().getJwtToken();
        const refreshToken = userSession?.getRefreshToken().getToken();

        if(idToken === undefined || accessToken === undefined || refreshToken === undefined) {
            throw new Error("idToken, accessToken, or refreshToken is undefined");
        }

        // 调用后端
        const processUserResult = await _userService.processUser(
            {
                IdToken: idToken,
                AccessToken: accessToken,
                RefreshToken: refreshToken,
            } as ProcessUser
        );

        console.log(user);
        console.log(processUserResult);
    } catch(error) {
        console.warn(error);
    } finally {
        // 无论成功失败,重置处理标记
        setIsProcessing(false);
    }
};

同时在事件回调中也加入判断:

Hub.listen("auth", ({ payload: { event, data }}) => {
    switch (event) {
        case 'signIn':
        case 'cognitoHostedUI':
            if (!isProcessing) {
                getUserAndProcess(authListenerCancelToken).catch(console.error);
            }
            break;
        case "signOut":
            setUser(null);
            break;
        case 'signIn_failure':
        case 'cognitoHostedUI_failure':
            console.log('Sign in failure', data);
            break;
    }
});

思路2:提前过滤重复事件

Amplify的Auth事件有时会因内部状态同步触发多次,你可以通过用户唯一标识(如sub或username)过滤重复事件:

// 组件内新增状态记录已处理的用户ID
const [processedUserId, setProcessedUserId] = useState<string | null>(null);

Hub.listen("auth", ({ payload: { event, data }}) => {
    switch (event) {
        case 'signIn':
        case 'cognitoHostedUI':
            const userId = data?.sub || data?.username;
            // 只有用户ID存在且未处理过时才执行逻辑
            if (userId && userId !== processedUserId) {
                getUserAndProcess(authListenerCancelToken).catch(console.error);
                setProcessedUserId(userId);
            }
            break;
        case "signOut":
            setUser(null);
            setProcessedUserId(null); // 登出后重置标记
            break;
        case 'signIn_failure':
        case 'cognitoHostedUI_failure':
            console.log('Sign in failure', data);
            break;
    }
});

思路3:调整取消监听的时机

不要等到currentAuthenticatedUser返回后才取消监听,而是在事件回调的第一时间就检查是否已有认证用户,有则直接取消监听:

Hub.listen("auth", async ({ payload: { event, data }}) => {
    switch (event) {
        case 'signIn':
        case 'cognitoHostedUI':
            try {
                // 先检查是否已有认证用户
                const existingUser = await Auth.currentAuthenticatedUser();
                if (existingUser) {
                    authListenerCancelToken();
                    return;
                }
            } catch (e) {
                // 无用户,继续执行逻辑
            }
            getUserAndProcess(authListenerCancelToken).catch(console.error);
            break;
        // 其他case逻辑不变
    }
});

额外优化:合并初始调用与事件监听逻辑

你在useEffect中直接调用getUserAndProcess可能和事件触发逻辑重复,可调整为仅在无认证用户时添加监听:

useEffect(() => {
    let authListenerCancelToken: (() => void) | null = null;

    const initAuth = async () => {
        try {
            // 初始检查是否有用户
            const currentUser = await Auth.currentAuthenticatedUser() as CognitoUser;
            setUser(currentUser);
            // 直接调用后端,无需监听事件
            await _userService.processUser(/* 传入token参数 */);
        } catch (e) {
            // 无用户时添加事件监听
            authListenerCancelToken = Hub.listen("auth", ({ payload: { event, data }}) => {
                // 事件处理逻辑
            });
        }
    };

    initAuth().catch(console.error);

    // 组件卸载时取消监听
    return () => authListenerCancelToken?.();
}, []);

内容的提问来源于stack exchange,提问作者FailedUnitTest

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 04:04:57