在同一域上的站点之间共享 cookie - Headless / Decoupled CMS
Posted
技术标签:
【中文标题】在同一域上的站点之间共享 cookie - Headless / Decoupled CMS【英文标题】:Sharing cookies between sites on the same domain - Headless / Decoupled CMS 【发布时间】:2018-12-20 18:56:03 【问题描述】:我的挑战背景
我正在建立一个无头 WordPress / WooCommerce 商店。
如果您不熟悉无头 CMS 的概念,我会通过 WordPress/WooCommerce REST API 提取商店的内容(产品及其图像、文本)。通过这种方式,我可以为我的客户受益于 CMS 仪表板,同时我可以使用现代语言/库进行开发,就我而言 - React!
如果可能的话,我想在 WordPress/WooCommerce/php 中保留结帐。根据我应用此代码/样板的项目,我怀疑我将不得不削减和更改支付网关,并且在 PHP/WordPress 中使这种安全和 PCI 兼容会更容易 - 有很多插件可以用于此。
这意味着整个商店/前端都将存在于 React 中,但购物车除外,当用户希望完成订单时,他们将在购物车中被重定向到 CMS 前端(WordPress、PHP)。
挑战
这使得管理会话的 cookie 变得相当不直观和不正统。当用户从商店(React 站点)重定向到结帐(WooCommerce/PHP 站点)时,购物车会话必须在两个站点之间持续存在。
此外,对 WooCommerce 的请求通过我的 React 客户端所在的 Node/Express 服务器进行路由。我这样做是因为我想隐藏 WordPress 地址,因此我可以应用 GraphQL 来清理我的请求和响应。 这个问题是在这个过程中,cookie 丢失了,因为我的客户端和我的 CMS 通过中间人(我的节点服务器)进行通信 - 我需要额外的逻辑来手动管理我的 cookie。
守则
当我尝试从动作创建者(我使用 Redux 进行状态管理)向购物车添加东西时,我点击了我的 Node/Express 服务器上的 api 对应端点:
export const addToCart = (productId, quantity) => async (dispatch) =>
dispatch(type: ADD_TO_CART);
try
// Manually append cookies somewhere here
const payload = await axios.get(`$ROOT_API/addtocart?productId=$productId&quantity=$quantity`,
withCredentials: true
);
dispatch(addToSuccess(payload));
catch (error)
dispatch(addToCartFailure(error));
;
然后在 Node/Express 服务器上我向 WooCommerce 发出请求:
app.get('/api/addtocart', async (req, res) =>
try
// Manually retrieve & append cookies somewhere here
const productId = parseInt(req.query.productId);
const quantity = parseInt(req.query.quantity);
const response = await axios.post(`$WP_API/wc/v2/cart/add`,
product_id: productId,
quantity
);
return res.json(response.data);
catch (error)
// Handle error
return res.json(error);
);
【问题讨论】:
您是否尝试在您的wp-config.php
中设置define( 'COOKIE_DOMAIN', 'somedomain.com' );
,这将确保wordpress 在主域而不是子域中生成cookie,并且它们都将与任何额外的cookie 共享,所以没有代码像你现在这样的复杂性
感谢@TarunLalwani 的提示-我还没有尝试过,我会试一试!如果这能解决我所有的饼干问题,那就太棒了。我最初的犹豫是 A) WooCommerce 是否会遵守此选项,以及 B) 从 React 到 WordPress 的所有 REST API 调用都是在服务器端(节点, express),而不是客户端(react)。我想这会妨碍 axios(我的 http 客户端)自动将相关的浏览器 cookie 添加到我发送到 WordPress 的请求中
A) 可以消除 - 我发现开发人员确认:“WooCommerce 使用与 WordPress 相同的 cookie 设置(例如 WordPress 登录 cookie)”
对于 B,您需要将您的请求中的所有浏览器 cookie 发送到 WP,并为返回 cookie 做同样的事情
你是对的,我必须在访问 CMS(WooCommerce via REST)之前将我的 cookie 从客户端(React)传递到服务器(Node),然后以相反的方式设置来自节点服务器内的客户端的 cookie。这很复杂,但它几乎就在那里!
【参考方案1】:
借助@TarunLalwani 在他的 cmets 中提供的线索(感谢一百万!),我设法制定了解决方案。
Cookie 域设置
由于我在使用两个单独的站点,为了使这个工作能够正常工作,我必须确保它们都在同一个域中,并且在所有 cookie 中都设置了域。这确保了 cookie 包含在我在 Node / Express 服务器(例如,somedomain.com
)和 WooCommerce CMS(例如,wp.somedomain.com
)之间的请求中,而不是 wp.somedomain
子域独有的。这是通过在我的 CMS 上的 wp-config.php
中设置 define( 'COOKIE_DOMAIN', 'somedomain.com' );
来实现的。
手动获取和设置 Cookies
我的代码需要大量额外的逻辑,以便在请求通过客户端通过我的 Node / Express 服务器路由时包含 cookie。
在 React 中,我必须检查 cookie 是否存在,如果存在,我必须在 GET 请求的标头中将其发送到 Node / Express 服务器。
import Cookies from 'js-cookie';
export const getSessionData = () =>
// WooCommerce session cookies are appended with a random hash.
// Here I am tracking down the key of the session cookie.
const cookies = Cookies.get();
if (cookies)
const cookieKeys = Object.keys(cookies);
for (const key of cookieKeys)
if (key.includes('wp_woocommerce_session_'))
return `$key=$Cookies.get(key);`;
return false;
;
export const addToCart = (productId, quantity) => async (dispatch) =>
dispatch(type: ADD_TO_CART);
const sessionData = getSessionData();
const config = ;
if (sessionData) config['session-data'] = sessionData;
console.log('config', config);
try
const payload = await axios.get(`$ROOT_API/addtocart?productId=$productId&quantity=$quantity`,
withCredentials: true,
headers: config
);
dispatch(addToSuccess(payload));
catch (error)
dispatch(addToCartFailure(error));
;
在 Node / Express 服务器上,我必须检查我是否包含来自客户端的 cookie(保存在 req.headers
中,密钥为 session-data
- 在此处使用 Cookie
作为密钥是非法的),并且如果我这样做了,请将其附加到我的 CMS 请求的标题中。
如果我没有找到附加的 cookie,这意味着这是会话中的第一个请求,所以我必须从我从 CMS 返回的响应中手动获取 cookie 并将其保存到客户端 (@987654332 @)。
app.get('/api/addtocart', async (req, res) =>
try
const productId = parseInt(req.query.productId);
const quantity = parseInt(req.query.quantity);
const sessionData = req.headers['session-data'];
const headers = ;
if (sessionData) headers.Cookie = sessionData;
const response = await axios.post(`$WP_API/wc/v2/cart/add`,
product_id: productId,
quantity
, headers );
if (!sessionData)
const cookies = response.headers['set-cookie'];
const setCookieFunc = (cookie) =>
const [cookieKeyValue, ...cookieOptionsArr] = cookie.split('; ');
const cookieKey = cookieKeyValue.split('=')[0];
const cookieValue = decodeURIComponent(cookieKeyValue.split('=')[1]);
const cookieOptions = ;
cookieOptionsArr.forEach(option => (cookieOptions[option.split('=')[0]] = option.split('=')[1]));
if (cookieOptions.expires)
const expires = new Date(cookieOptions.expires);
cookieOptions.expires = expires;
res.cookie(cookieKey, cookieValue, cookieOptions);
;
cookies.map(cookie => setCookieFunc(cookie));
return res.json(response.data);
catch (error)
// Handle error
return res.json(error);
);
我不确定这是否是解决问题的最优雅的方法,但它对我有用。
备注
我使用 js-cookie 库与我的 React 客户端上的 cookie 进行交互。
陷阱
如果您尝试在您的开发环境(使用本地主机)中完成这项工作,则需要完成一些额外的工作。见Cookies on localhost with explicit domain
【讨论】:
以上是关于在同一域上的站点之间共享 cookie - Headless / Decoupled CMS的主要内容,如果未能解决你的问题,请参考以下文章