登录后API调用无法保留PHP Session问题求助
场景
本地运行的PHP应用,通过Cypress的before:run事件使用Axios调用API植入测试数据。已实现数据库植入方式,切换到API方式后遇到会话维持问题:登录API调用成功并返回PHPSESSID,但后续API调用未携带该会话ID,导致无法识别登录状态,无法执行需要权限的操作(如创建邀请)。
关键现象
- 登录请求返回
PHPSESSID=gp9329kmpfili2r49rl3hqc3no,但后续请求生成了新的PHPSESSID=00demprhj3anp6ketbvlkpo36e - 后续API返回
{"PHP > logged in?": false},证明会话未关联
已尝试的方案
- Axios配置:设置
axios.defaults.withCredentials = true,添加请求/响应拦截器查看头信息 - .htaccess配置CORS:允许跨域凭证、指定允许的Origin和头信息
- PHP Session参数配置:设置
secure=true、samesite=None等Cookie参数
额外解决方法
1. 复用Axios单例实例确保Cookie共享
Axios默认在同一个实例中维护Cookie,若静态方法调用时未复用实例,会导致Cookie丢失。修改ApiUtilities类使用单例Axios实例:
import axios from 'axios' // 创建单例Axios实例并配置 const apiClient = axios.create({ withCredentials: true, headers: { 'Content-Type': 'application/json' } }) apiClient.interceptors.request.use( (config) => { console.log('Sent headers:', config.headers) return config }, (error) => Promise.reject(error) ) export const enum ApiCallMethods { POST } export default class ApiUtilities { static async callApi(url: string, callMethod: ApiCallMethods, data: object): Promise<any> { let response try { switch (callMethod) { case ApiCallMethods.POST: response = await apiClient.post(url, data) break } console.log('Received headers:', response.headers) console.log('Data:', response.data) return response.data } catch (error) { throw error } } static async logIn() { await this.callApi('https://localhost/xyz/php/apis/users.php', ApiCallMethods.POST, { action: 'log_in', username: 'censored', password: 'censored' }) } }
2. 手动提取并携带PHPSESSID
若自动Cookie管理失效,手动从登录响应中提取PHPSESSID,后续请求通过Cookie头携带:
import axios from 'axios' const apiClient = axios.create({ withCredentials: true, headers: { 'Content-Type': 'application/json' } }) export const enum ApiCallMethods { POST } export default class ApiUtilities { private static sessionId: string | null = null static async logIn() { const response = await apiClient.post('https://localhost/xyz/php/apis/users.php', { action: 'log_in', username: 'censored', password: 'censored' }) // 提取set-cookie中的PHPSESSID const setCookieHeader = response.headers['set-cookie'] if (setCookieHeader) { const sessionCookie = setCookieHeader.find((cookie: string) => cookie.startsWith('PHPSESSID=')) if (sessionCookie) { this.sessionId = sessionCookie.split(';')[0].split('=')[1] } } return response.data } static async callApi(url: string, callMethod: ApiCallMethods, data: object): Promise<any> { const config: any = {} if (this.sessionId) { config.headers = { Cookie: `PHPSESSID=${this.sessionId}` } } let response try { switch (callMethod) { case ApiCallMethods.POST: response = await apiClient.post(url, data, config) break } console.log('Received headers:', response.headers) console.log('Data:', response.data) return response.data } catch (error) { throw error } } }
3. 使用tough-cookie手动管理Cookie Jar
Cypress的before:run运行在Node.js环境,Axios默认Cookie处理可能存在跨域问题,引入tough-cookie库手动管理:
import axios from 'axios' import { CookieJar } from 'tough-cookie' import { wrapper } from 'axios-cookiejar-support' // 创建Cookie Jar并绑定到Axios实例 const cookieJar = new CookieJar() const apiClient = wrapper(axios.create({ withCredentials: true, jar: cookieJar, headers: { 'Content-Type': 'application/json' } })) // 其余代码复用单例实例方案
4. 检查PHP会话存储目录权限
PHP会话默认存储在系统临时目录(如/tmp或Windows临时文件夹),若目录无读写权限,PHP无法保存会话文件,会每次生成新PHPSESSID。可在PHP中执行echo session_save_path();查看路径,确保Apache/PHP进程对该目录有读写权限。
5. 禁用本地HTTPS证书验证(仅测试用)
本地自签名HTTPS证书会被Node.js默认拒绝,导致Cookie无法存储。可在Axios实例中临时禁用SSL验证:
import axios from 'axios' import https from 'https' const apiClient = axios.create({ withCredentials: true, httpsAgent: new https.Agent({ rejectUnauthorized: false }), headers: { 'Content-Type': 'application/json' } })
注意:仅本地测试使用,生产环境绝对不能启用此配置
6. 检查Cypress全局Cookie清除配置
确认cypress.config.js中无experimentalSessionAndOrigin或clearCookiesBeforeRun等会提前清除Cookie的配置,避免影响会话维持。
内容的提问来源于stack exchange,提问作者Zuno

