如何为PHP项目配置Sessions跨域?

wen PHP项目 2

本文目录导读:

如何为PHP项目配置Sessions跨域?

  1. 方案一:基于Cookie的通用跨域(适用于主域名相同)
  2. 方案二:完全不同的顶级域名(如 example.com 与 other.com)
  3. 方案三:基于Token的共享(推荐用于完全跨域)
  4. 方案四:iframe + postMessage(特殊场景)
  5. 总结选择建议

为PHP项目配置Session跨域,通常涉及两个核心问题:跨域请求携带Session ID(如Cookie跨域)和跨域共享Session数据(如不同子域名共享登录态)。

由于HTTP协议限制和浏览器安全策略,Session跨域有几种主流解决方案,根据项目架构选择合适方案:

基于Cookie的通用跨域(适用于主域名相同)

场景a.example.comb.example.com 共享Session。

设置顶级域名Cookie

在PHP中设置Session时,指定Cookie域为.example.com

// 在所有应用入口统一设置
session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'domain' => '.example.com', // 注意前缀点号
    'secure' => false,          // HTTPS时设为true
    'httponly' => true,
    'samesite' => 'Lax'         // 需要Lax或None(HTTPS必须为None)
]);
session_start();

配置php.ini

修改 session.cookie_domain

session.cookie_domain = ".example.com"

共享Session存储

不同子域名必须使用同一个Session存储后端(默认文件存储会失败,因为文件路径不同),推荐使用:

  • RedisMemcached:最快的共享方案
  • 数据库:更易控制

使用Redis示例:

// 共享的Session配置(建议用框架或统一初始化文件)
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', 'tcp://127.0.0.1:6379?prefix=PREFIX_');
// 或使用PHP代码
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
session_set_save_handler(
    function($savePath, $sessionName) { return true; },
    function() { return true; },
    function($sessionId) {
        global $redis;
        $data = $redis->get("SESSION:$sessionId");
        return $data ?: '';
    },
    function($sessionId, $data) {
        global $redis;
        $redis->setex("SESSION:$sessionId", 3600, $data);
        return true;
    },
    function($sessionId) {
        global $redis;
        $redis->del("SESSION:$sessionId");
        return true;
    },
    function($maxlifetime) {
        // GC由Redis自动处理
        return true;
    }
);
session_start();

注意:如果不共享存储,即使Cookie跨域成功,不同服务器/域名的Session数据也不同。

完全不同的顶级域名(如 example.com 与 other.com)

浏览器默认禁止跨域Cookie,需要:

  1. SameSite=None + Secure(必须HTTPS)
  2. CORS 配置(Access-Control-Allow-Credentials)

PHP响应头示例:

header('Access-Control-Allow-Origin: https://other.com');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Headers: Content-Type');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
// 处理OPTIONS预检请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}
// Session配置
session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'domain' => '.example.com',  // 这里无法解决不同顶级域名
    'secure' => true,            // 必须
    'httponly' => true,
    'samesite' => 'None'        // 必须
]);
session_start();

注意:不同顶级域名无法通过Cookie本身完成共享,需要以下替代方案。

基于Token的共享(推荐用于完全跨域)

不使用传统PHP Session,改用JWT自定义Token

登录成功后生成Token

function generateToken($userId) {
    $payload = [
        'user_id' => $userId,
        'exp' => time() + 3600,
        'iat' => time()
    ];
    // 使用JWT库(如 firebase/php-jwt)
    $token = JWT::encode($payload, 'your-secret-key', 'HS256');
    return $token;
}

前端存储Token(localStorage或Cookie)

// 登录成功后
localStorage.setItem('auth_token', token);
// 或设置Cookie(SameSite=None + Secure)
// 每次请求携带
fetch('https://api.example.com/data', {
    headers: {
        'Authorization': 'Bearer ' + token
    },
    credentials: 'include' // 如果使用Cookie方式
});

PHP验证Token

function validateToken() {
    $headers = getallheaders();
    $token = $headers['Authorization'] ?? '';
    $token = str_replace('Bearer ', '', $token);
    try {
        $payload = JWT::decode($token, 'your-secret-key', ['HS256']);
        return $payload->user_id;
    } catch (Exception $e) {
        http_response_code(401);
        exit('Unauthorized');
    }
}

iframe + postMessage(特殊场景)

如果A域页面通过iframe嵌入B域页面,可以利用postMessage传递Session ID:

B域(提供API):

// B域监听postMessage
window.addEventListener('message', function(event) {
    if (event.origin !== 'https://a.com') return;
    // 收到A域发来的Session ID
    var sessionId = event.data;
    // 使用该Session ID请求数据
    fetch('/api/data', {
        headers: {'X-Session-ID': sessionId}
    });
});

总结选择建议

场景 推荐方案 复杂度
同主域不同子域 Cookie + 共享Session存储
不同主域(内网/可控环境) SameSite=None + CORS
完全跨域(生产环境) Token/JWT认证
需要维护传统Session 反向代理统一域名(如Nginx)

最推荐:现代项目中尽量使用Token认证替代Session,天然跨域友好,如果必须用Session,优先确保共享存储(Redis)和Cookie域正确配置。

抱歉,评论功能暂时关闭!