如题,最近做的项目,app下有多个应用目录,要求其中两个应用目录共用session(本项目中是要实现新的api目录共用旧的home目录的session),其他应用目录各自用各自的session。
基本实现逻辑是需要保证两个模块的 Session 存储规则、命名空间、Cookie 标识完全一致(消除隔离因素)。
具体配置和操作步骤如下:
一、核心原则(共享的前提)
- 两个模块使用相同的 Session 前缀(或都不设置前缀);
- 两个模块使用相同的 Session 名称(session_name)(决定浏览器 Cookie 标识);
- 两个模块的 Session 存储驱动、存储地址一致(如都用文件 / Redis,且路径 / 库相同);
- 确保请求能携带 Session ID(浏览器自动带 Cookie)。
二、具体配置步骤
步骤 1:统一两个模块的 Session 配置 (* 重要)
可选方法1):删除 api 和 home 模块下独立的 session.php 配置(如果有),让两个模块共用项目根目录 的 config/session.php 全局配置(核心是统一前缀、名称)。
可选方法2):因本项目需要保留其他应用不同的session配置,所以需要保留模块独立配置文件,则强制让 api/config/session.php 和 home/config/session.php 的核心参数完全一致:
// app/api/config/session.php 和 app/home/config/session.php 内容完全一致
return [
// 1. 统一前缀(关键:必须相同,或都为空)
'prefix' => 'shared_', // 比如统一用 shared_ 前缀,或直接留空
// 2. 统一 Session 名称(Cookie 标识,默认 PHPSESSID,必须相同)
'name' => 'PHPSESSID',
// 3. 统一存储驱动(比如都用文件,或都用 Redis)
'type' => 'file',
// 4. 自动启动 Session(必须开启)
'auto_start' => true,
// 其他配置(路径、过期时间等)也保持一致
'expire' => 7200,
//session存储路径
'path' => env('SESSION_PATH', str_replace('api', 'home', runtime_path()) . 'session/'),
];
步骤 2:确保 Session ID 能跨模块传递
Session 共享的本质是两个模块使用同一个 Session ID,不同场景处理方式不同:
场景 1:浏览器访问(home + API 接口通过浏览器 /ajax 调用)
【本项目为此种情况,所以两个项目直接浏览器访问接口即可共享SESSION,无需场景2的复杂设置】
浏览器会自动携带 PHPSESSID Cookie,无需额外处理!
- 比如:home 模块登录后写入
session('user_id', 100),前端通过 ajax 调用 api 模块接口时,浏览器会自动把PHPSESSID传给 API 接口,API 模块可直接读取session('user_id')。
场景 2:非浏览器访问 API(如移动端 / 小程序调用)
移动端 / 小程序没有 Cookie 机制,无法自动携带 PHPSESSID,需手动传递 Session ID:
-
home 模块登录后,返回 Session ID 给前端:
// home 模块登录控制器 public function login() { // 验证账号密码后写入 Session session('user_id', 100); // 返回 Session ID 给前端(让前端存储,后续调用 API 时携带) return json([ 'code' => 200, 'msg' => '登录成功', 'data' => [ 'session_id' => session_id(), // 获取当前 Session ID 'user_id' => session('user_id') ] ]); } -
API 模块接收并设置 Session ID :前端调用 API 时,把
session_id放在请求头(推荐)、参数或 Cookie 中,API 模块手动设置 Session ID:// api 模块基础控制器(app/api/controller/BaseController.php) namespace app\api\controller; use think\Controller; class BaseController extends Controller { public function initialize() { parent::initialize(); // 方式1:从请求头获取 Session ID(推荐,比如 header 里的 X-Session-Id) $sessionId = $this->request->header('X-Session-Id'); // 方式2:从 GET/POST 参数获取(如 ?session_id=xxx) // $sessionId = $this->request->param('session_id'); // 手动设置 Session ID,实现共享 if (!empty($sessionId)) { session_id($sessionId); // 关键:设置为 home 模块的 Session ID } // 启动 Session(确保自动启动开启,或手动启动) session_start(); } } -
API 模块读取共享的 Session :所有 API 控制器继承上述
BaseController,即可直接读取 home 模块写入的 Session:// api 模块用户控制器 namespace app\api\controller; class User extends BaseController { // 获取当前登录用户信息(共享 home 模块的 Session) public function getUserInfo() { $userId = session('user_id'); // 能读取到 home 模块写入的 100 if (!$userId) { return json(['code' => 401, 'msg' => '未登录']); } return json([ 'code' => 200, 'msg' => 'success', 'data' => ['user_id' => $userId] ]); } }
三、验证共享是否生效
-
home 模块写入 Session:
// app/home/controller/Index.php public function setSession() { session('user_name', 'test_user'); // 写入共享 Session return json(['code' => 200, 'msg' => 'Session 已写入']); } -
api 模块读取 Session:
// app/api/controller/Index.php(继承 BaseController) public function getSession() { $userName = session('user_name'); // 应返回 "test_user" return json(['code' => 200, 'data' => ['user_name' => $userName]]); }
四、常见问题排查(共享失败时)
-
Session 读取为空:
- 检查两个模块的
prefix是否一致(比如一个设了home_,一个设了api_,会导致键名不同); - 检查
session_name是否一致(比如一个是PHPSESSID,一个是APISESSID,Cookie 标识不同); - 非浏览器场景,确认是否手动传递并设置了
session_id。
- 检查两个模块的
-
存储驱动问题:
- 如果用 Redis 存储,确保两个模块的 Redis 配置(主机、端口、库)一致;
- 如果用文件存储,确保
runtime/session目录有读写权限。
-
跨域请求导致 Cookie 丢失(AJAX 调用 API):前端跨域调用 API 时,需配置:
-
前端:AJAX 请求添加
withCredentials: true(允许携带 Cookie); -
后端:API 模块添加跨域头,允许携带凭证:
// api 模块 BaseController 的 initialize 中 header('Access-Control-Allow-Origin: ' . $this->request->header('Origin')); header('Access-Control-Allow-Credentials: true'); // 关键:允许携带 Cookie header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE'); header('Access-Control-Allow-Headers: X-Session-Id, Content-Type');
-
总结
让 api 和 home 模块共享 Session 的核心是:
- 统一 Session 配置(前缀、名称、驱动);
- 保证请求携带相同的 Session ID(浏览器自动带,非浏览器手动传)。
浏览器场景只需统一配置即可;非浏览器(移动端)需额外传递 session_id 并手动设置,就能实现完美共享。