用于独立用户会话的 PHP cURL Cookie 罐
围绕远程会话设计 PHP cURL Cookie 持久化:可信的文件名、显式保存与并发请求。
一个 PHP 应用同时服务 Alice 和 Bob,两人都用它的服务端 cURL 客户端经代理访问同一个远程站点。如果每个请求都加载 cookies.txt,两个用户就可能继承同一个远程登录。 更换他们的代理地址并不能把这个文件分开。
在选文件名之前先选 Cookie 的归属。通常归属是应用的逻辑远程会话:一个本地用户、一个特定的远程账户,必要时再加一个独立工作区。代理端点只是路由选择,不是可靠的会话标识。多个用户可以共用一个端点,而端点还可能轮换出口。
加载并保存同一会话的 Cookie 罐
让应用把已认证会话映射到服务端生成的文件键。在私有 Cookie 目录和可信的 $sessionKey 下,cURL 配置大致如下:
$jar = $cookieDirectory . '/' . $sessionKey . '.cookies';
$ch = curl_init('https://service.example.net/account');
curl_setopt_array($ch, [
CURLOPT_PROXY => 'http://proxy.example.net:8080',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEFILE => $jar,
CURLOPT_COOKIEJAR => $jar,
]);
如果要在一台允许公共中间人的网络上做获准的公共站点会话测试,HTTP 代理列表可以提供候选端点。使用未配置代理认证、也未保留代理凭据的句柄,并把 Cookie 状态固定分配给预期会话。这段代码只是配置句柄;传输由应用执行,并通过它正常的请求流程检查结果。COOKIEFILE 加载该会话此前的 Cookie。COOKIEJAR 决定句柄把保留的 Cookie 写到哪里。只设置输出文件并不会载入此前的登录。
新会话可以在没有文件的情况下开始。给它第一次传输指定预期的罐路径,让网站建立 Cookie,再把结果状态保存下来供下一次请求使用。Cookie 目录必须对应用的工作进程可写,并放在对外提供的文档树之外。不要把访客提供的用户名或路径变成文件名;可信的键要在服务端查。
保存需要在请求生命周期中占一个位置
libcurl 通常在清理句柄时写罐。如果应用需要在远程交互之后有一个显式的保存点,可以请求:
curl_setopt($ch, CURLOPT_COOKIELIST, 'FLUSH');
这会把句柄当前已知的 Cookie 写入其配置的罐。先结束该句柄在这个会话上的使用,再把它分配给不相关的工作。仅仅在一个已经装着 Alice 的 Cookie 的句柄上改罐路径,并不等于创建 Bob 的干净会话。
还要区分下载成功和持久化成功。Cookie 罐的文档指出,清理期间的写入失败不会作为普通传输错误返回。当登录对一次请求有效、到下一次却消失时,要检查目录归属、权限以及应用的诊断输出。
并发请求带来另一条边界。两个工作进程可以加载同一个旧罐,各自独立修改内存中的状态,然后保存不同的结果。如果两个请求共用一个远程登录,按会话加锁或等价的串行化就必须覆盖整个加载、交换和保存序列;只锁最后一次写入并不能调和它们此前的快照。不同会话应当使用不同文件和独立的锁。
登出时,结束预期的远程会话,并按照应用策略退役它的本地 Cookie 状态。这样新选的代理就可以挂到用户开启的任意新会话上,而不必让代理地址来决定内存里是谁的登录。