TP6 app下的两个应用目录如何共用一个SESSION

如题,最近做的项目,app下有多个应用目录,要求其中两个应用目录共用session(本项目中是要实现新的api目录共用旧的home目录的session),其他应用目录各自用各自的session。

基本实现逻辑是需要保证两个模块的 Session 存储规则、命名空间、Cookie 标识完全一致(消除隔离因素)。

具体配置和操作步骤如下:

一、核心原则(共享的前提)

  1. 两个模块使用相同的 Session 前缀(或都不设置前缀);
  2. 两个模块使用相同的 Session 名称(session_name)(决定浏览器 Cookie 标识);
  3. 两个模块的 Session 存储驱动、存储地址一致(如都用文件 / Redis,且路径 / 库相同);
  4. 确保请求能携带 Session ID(浏览器自动带 Cookie)。

二、具体配置步骤

步骤 1:统一两个模块的 Session 配置 (* 重要)

可选方法1):删除 apihome 模块下独立的 session.php 配置(如果有),让两个模块共用项目根目录config/session.php 全局配置(核心是统一前缀、名称)。

可选方法2):因本项目需要保留其他应用不同的session配置,所以需要保留模块独立配置文件,则强制让 api/config/session.phphome/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:

  1. 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')
            ]
        ]);
    }
  2. 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();
        }
    }
  3. 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]
            ]);
        }
    }

三、验证共享是否生效

  1. home 模块写入 Session

    复制代码
    // app/home/controller/Index.php
    public function setSession()
    {
        session('user_name', 'test_user'); // 写入共享 Session
        return json(['code' => 200, 'msg' => 'Session 已写入']);
    }
  2. 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]]);
    }

四、常见问题排查(共享失败时)

  1. Session 读取为空

    • 检查两个模块的 prefix 是否一致(比如一个设了 home_,一个设了 api_,会导致键名不同);
    • 检查 session_name 是否一致(比如一个是 PHPSESSID,一个是 APISESSID,Cookie 标识不同);
    • 非浏览器场景,确认是否手动传递并设置了 session_id
  2. 存储驱动问题

    • 如果用 Redis 存储,确保两个模块的 Redis 配置(主机、端口、库)一致;
    • 如果用文件存储,确保 runtime/session 目录有读写权限。
  3. 跨域请求导致 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');

总结

apihome 模块共享 Session 的核心是:

  1. 统一 Session 配置(前缀、名称、驱动);
  2. 保证请求携带相同的 Session ID(浏览器自动带,非浏览器手动传)。

浏览器场景只需统一配置即可;非浏览器(移动端)需额外传递 session_id 并手动设置,就能实现完美共享。

相关推荐
ZZDICT1 年前
Tomcat session复制及session共享技术
tomcat·session共享
ZZDICT1 年前
基于redisson实现tomcat集群session共享
redis·tomcat·session共享
半部论语2 年前
第十四章 : Spring Boot 整合spring-session,使用redis共享
spring boot·分布式session·session共享·session redis
雾林小妖2 年前
分布式环境下的session 共享-基于spring-session组件和Redis实现
分布式session·spring-session·redissession·session共享
胡西风_foxww2 年前
thinkphp6(tp6)创建定时任务
定时任务·thinkphp6·tp6