让 TP6 的 Session 组件重新接管控制权
我们需要让 TP6 的 Session 组件重新接管控制权,同时强行将其配置到 Redis 2号库,并在入口处拦截并纠正 APP 带来的 26 位 Token,最后同步写到 6号库。
请严格按照以下步骤进行调整,无需修改你现有的 session() 读写业务代码:
第一步:恢复 TP6 的 Session 中间件
打开全局中间件配置文件 app/middleware.php。
确保将之前注释掉的这行代码恢复启用(如果本来就是启用的,请保持原样):
php
\think\middleware\SessionInit::class,
请谨慎使用此类代码。
第二步:修改 TP6 的 Session 配置文件
打开 config/session.php,删除或注释掉原有的配置,完全替换为以下内容。这会引导 session() 函数直连 Redis 2号库,并自动从 POST 参数中寻找 token:
php
return [
// 使用 Redis 驱动
'type' => 'redis',
// 缓存前缀(如果你希望 Redis 中的键名直接是 32 位 Token,可以留空 '')
'prefix' => '',
// Session 有效期(秒)
'expire' => 1440,
// 关键配置:告诉 TP6 优先从 POST/GET 请求中的 'token' 字段来读取 session_id
'var_session_id' => 'token',
// Redis 连接参数
'host' => '127.0.0.1',
'port' => 6379,
'password' => '', // 如果有密码请填写
'select' => 2, // 强制指定存入 2 号数据库
'timeout' => 0,
'persistent' => false,
];
请谨慎使用此类代码。
第三步:在基类控制器中做「32位清洗」与「6号库同步」
由于 TP6 的中间件在进入控制器之前就已经运行完毕,此时框架已经根据 APP 传过来的 token 进行了初始化。
我们需要在 BaseController(或专供 APP 调用的公共基类)的 initialize() 方法中,对初始化结果进行检查。如果发现是 26 位,直接废弃并重新生成 32 位,然后写入 6 号库:
php
namespace app\controller;
use think\App;
use think\exception\HttpResponseException;
class BaseController
{
protected $app;
protected $request;
public function __construct(App $app)
{
$this->app = $app;
$this->request = $this->app->request;
$this->initialize();
}
protected function initialize()
{
// 1. 获取 TP6 框架当前识别到的 session_id
$sessionId = $this->request->session()->getId();
// 2. 【核心清洗】如果长度不是 32 位,或者格式不对(比如 APP 传过来的 26 位旧 Token)
if (strlen($sessionId) !== 32 || !preg_match('/^[a-f0-9A-Za-z]{32}$/', $sessionId)) {
// 强行生成一个绝对标准的 32 位 16 进制字符串
$new32Id = bin2hex(random_bytes(16));
// 强制将当前请求的 Session ID 切换为这个全新的 32 位 ID
$this->request->session()->setId($new32Id);
// 重新初始化一次 Session 数据,确保新 ID 立即在 2 号库生效
$this->request->session()->init();
$sessionId = $new32Id;
}
// 3. 【同步写入】此时 $sessionId 100% 是 32 位,直接同步到 6 号库
$this->syncToOnlineStats($sessionId);
}
/**
* 同步在线状态到 6 号库
*/
protected function syncToOnlineStats($sessionId)
{
try {
$redisDb6 = new \Redis();
$redisDb6->connect('127.0.0.1', 6379);
$redisDb6->select(6); // 切换到 6 号库
// 保持键名与 2 号库的 32 位 ID 绝对一致
$onlineKey = "app_online:" . $sessionId;
// 写入 6 号库并设置过期时间(例如 15 分钟)
$redisDb6->setex($onlineKey, 900, time());
} catch (\Exception $e) {
trace('6号库在线统计写入失败: ' . $e->getMessage(), 'error');
}
}
/**
* 统一的接口返回方法
*/
protected function apiReturn($data = [], $msg = 'success', $code = 200)
{
// 这里必须用 return 显式输出,或者像之前一样抛出 HttpResponseException
$response = json([
'code' => $code,
'msg' => $msg,
'data' => $data,
// 关键:永远向 APP 返回当前框架生效的、写进双库的 32 位 Token
'token' => $this->request->session()->getId()
]);
throw new HttpResponseException($response);
}
}
请谨慎使用此类代码。
🏆 这样改造后的最终状态:
零业务代码修改:你散落在项目各处的
session('user_id')、session('info', $data)助手函数完全不需要动,它们会照常工作。数据绝对对齐:
2 号库中存储的键名将变成纯 32 位(或带有你在
config/session.php中指定的prefix前缀)。6 号库中存储的键名也同步变成了
app_online:32位Token。APP 客户端接收到的也是 32 位,下次 POST 传回来的也是 32 位,系统进入良性循环。