我创建了一个 React 应用程序,我尝试在浏览器的本地存储中设置一些键。我还设置了会话存储。当我在开发模式下运行我的应用程序时,它可以正常工作——我成功地在会话和本地存储中设置了密钥。然而,在生产中,只有会话存储成功设置.
这是我的代码:
import React, { useEffect } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { connect } from 'react-redux';
import type { PayloadAction } from '@reduxjs/toolkit';
import type { IAutoAuthResponseData, ICliAuthResponseData } from '@exlint.io/common';
import { authActions } from '@/store/reducers/auth';
import type { IAuthPayload } from '@/store/interfaces/auth';
import BackendService from '@/services/backend';
import CliBackendService from '@/services/cli-backend';
import type { AppState } from '@/store/app';
import ExternalAuthRedirectView from './ExternalAuthRedirect.view';
interface IPropsFromState {
readonly isAuthenticated: boolean | null;
}
interface PropsFromDispatch {
readonly auth: (loginPayload: IAuthPayload) => PayloadAction<IAuthPayload>;
}
interface IProps extends IPropsFromState, PropsFromDispatch {}
const ExternalAuthRedirect: React.FC<IProps> = (props: React.PropsWithChildren<IProps>) => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const refreshToken = searchParams.get('refreshToken');
const port = searchParams.get('port');
useEffect(() => {
if (refreshToken) {
localStorage.setItem('token', refreshToken);
const fetchResults = async () => {
let autoAuthResponseData: IAutoAuthResponseData;
try {
autoAuthResponseData = await BackendService.get<IAutoAuthResponseData>('/user/auth');
sessionStorage.setItem('token', autoAuthResponseData.accessToken);
} catch {
localStorage.clear();
let navigateUrl = '/auth';
if (port) {
navigateUrl += `?port=${port}`;
}
navigate(navigateUrl);
return;
}
if (port) {
let cliToken: string;
let email: string;
try {
const responseData = await CliBackendService.get<ICliAuthResponseData>(
'/user/auth/auth',
);
cliToken = responseData.cliToken;
email = responseData.email;
} catch {
navigate(`/cli-auth?port=${port}`);
return;
}
try {
const res = await fetch(`http://localhost:${port}/${cliToken}/${email}`);
if (!res.ok) {
throw new Error();
}
navigate('/cli-authenticated');
} catch {
navigate(`/cli-auth?port=${port}`);
return;
}
}
props.auth({
id: autoAuthResponseData.id,
name: autoAuthResponseData.name,
createdAt: autoAuthResponseData.createdAt,
});
};
fetchResults();
} else {
let navigateUrl = '/auth';
if (port) {
navigateUrl += `?port=${port}`;
}
navigate(navigateUrl);
}
}, [refreshToken, port]);
useEffect(() => {
if (!port && props.isAuthenticated) {
navigate('/', { replace: true });
}
}, [port, props.isAuthenticated]);
return <ExternalAuthRedirectView />;
};
ExternalAuthRedirect.displayName = 'ExternalAuthRedirect';
ExternalAuthRedirect.defaultProps = {};
const mapStateToProps = (state: AppState) => {
return {
isAuthenticated: state.auth.isAuthenticated,
};
};
export default connect(mapStateToProps, { auth: authActions.auth })(React.memo(ExternalAuthRedirect));
如您所见,我有这行代码:
localStorage.setItem('token', refreshToken);
。
我可以保证它被执行,因为我看到一个 HTTP 请求从我的浏览器传出(并且代码确实在这行代码之后调用 HTTP 请求,如您所见)。
同样的代码,出于某种原因,没有设置本地存储。但是,您可以在接下来的代码行中看到,我还设置了会话存储:
sessionStorage.setItem('token', autoAuthResponseData.accessToken);
。然后,会话存储成功设置也在生产.
当我在生产环境中的浏览器中检查我的控制台时,我看到这条消息:
Storage access automatically granted for First-Party isolation “https://api.exlint.io” on “https://app.exlint.io”.
。不确定它有多相关。
存储绑定到源(域/协议/端口)。这意味着不同的协议或子域定义了不同的存储对象,它们不能访问彼此的数据。
我相信在生产模式下,问题与更改子域有关(app.exlint.io / app.exlint.io)。在 navigate() 调用之后,对 localStorage.getItem 的调用转到与另一个子域关联的 LocalStorage 对象。
我发现了一个 stackoverflow 问题,它有一个解决您问题的示例。解决方案是使用 iframe 和 postMessage 通过父域访问“共享”存储。