多数据源配置需在database.php中显式定义connections并正确调用env()读取对应环境变量,权限管理须实时校验、精确匹配路由规则、预加载rule避免N+1查询。

多数据源配置和多用户权限管理不能混着做,否则数据库连接错乱、权限校验失效是常态。 ThinkPHP 6 的 Db 类支持动态切换连接,但默认不自动感知角色上下文;权限系统若依赖中间件缓存或模糊规则名,上线后就会出现「明明分配了角色却提示无权限」这类问题。
如何在 .env 和 database.php 中正确配置多个数据库连接
ThinkPHP 6 不会自动读取 [DATABASE2] 这类节名,必须显式映射到 connections 数组里。直接写死连接参数虽能跑通,但无法复用 env() 配置,后续换环境要改两处。
-
database.php中的connections必须包含所有连接定义,例如'mysql2'而不只是'mysql' - 每个连接的
hostname、database等字段必须调用env('database2.hostname')而非env('database.hostname'),否则第二库永远读的是第一库配置 - 别在
connections里写deploy => 1或rw_separate => true,除非你真有主从结构——否则查从库时可能读到过期数据,且报错信息是SQLSTATE[HY000] [2002] Connection refused,容易误判为网络问题
为什么 Auth::check() 在角色变更后仍返回 false
权限判断结果被中间件缓存进 $this->request 或 Session 是最常见原因。比如用户刚被管理员后台分配新角色,刷新页面还是没权限,不是代码没生效,而是 Auth::check('admin/user/index') 拿的是上一次请求缓存的布尔值。
- 所有权限校验必须在控制器方法内实时执行,不要在
initialize()或中间件里一次性赋值给属性(如$this->hasPermission) - 避免使用
$this->request->has('auth_checked')这类标记跳过重复校验;每次请求都应调用Auth::getUser()->getRoles()动态获取角色列表 - 如果用了
think-auth扩展包,检查它是否把role_id写死进了 Session;应改为存user_id,每次查user_role表实时加载
rule 名必须精确到路由节点,不能用模块名或中文
写成 $auth->check('用户管理') 或 $auth->check('order') 看似省事,实际会导致权限无法回收、策略无法区分操作类型。ThinkPHP 的 check() 底层按字符串完全匹配,中文会被 URL 编码或转义,查不到记录。
立即学习“PHP免费学习笔记(深入)”;
- rule 名严格对应路由定义,例如
'admin/user/index'、'api/v1/order/export'、'admin/goods/edit' - 涉及行级控制的操作,加冒号后缀:如
'admin/order/view:own'表示只看自己创建的订单,解析逻辑需在自定义 check 方法里分发 - 关联表
role_rule必须带scope_type和scope_id字段,否则无法支持「某角色对部门 A 可编辑、对部门 B 仅可读」这类场景
性能瓶颈常出在 role_rule 表的 N+1 查询上
用户打开后台首页卡顿,debug 显示执行了 40+ 条 SQL,其中多数是反复查 role_rule —— 这说明权限校验没批量预加载,每次 check() 都单独查一次关联表。
- 在登录成功后,一次性查出该用户所有角色对应的全部 rule_id,存入 Redis 并设置带角色更新监听的过期时间
- 不要在模板里用
{if $auth->check('xxx')},这会触发多次查询;应在控制器中提前计算好权限状态数组传给视图 - 若必须动态判断,用
where(['rule_id' => ['in', $ruleIds]])替代循环调用check(),减少 DB 压力
真正麻烦的不是配两个库或建五张表,而是权限作用域(scope_type)、规则粒度(是否带冒号策略)、缓存生命周期这三者的组合逻辑。漏掉任意一个,都会让「已授权」变成「不可见」或「不可用」。



















