ThinkPHP6 API授权需实现JWT无状态鉴权、路由级控制与接口粒度权限码校验:签发时规范时间戳、配置化密钥、嵌入uid/role/permissions;中间件完成解析→验证→绑定→拦截四步闭环;路由分组显式绑定并排除登录等免鉴权路径;权限码命名规范、Redis缓存加速、响应字段过滤。

ThinkPHP6 添加 API 接口授权,核心是「无状态鉴权 + 路由级控制 + 权限动作校验」,不是简单加个中间件就完事。重点在于 JWT 签发与校验逻辑对齐、权限信息前置嵌入 token、以及接口粒度的权限码判断。
JWT 签发与密钥必须严格统一
授权的前提是可信凭证。签发 token 时务必做到:
- 使用 time() 生成 iat/exp 时间戳,避免 date('U') 或字符串时间造成解析失败
- 密钥从配置文件读取(如 config/jwt.php),禁止硬编码在中间件或服务类里
- 密钥字符串不能含空格、BOM、换行;推荐用 32 字符随机字符串,例如 8a3f5c9e2b1d7046f8a9c2e1d4b7f0a3
- payload 中至少包含 uid、role、permissions 数组(如 ["user:read", "order:create"]),敏感接口可额外加 scope 字段用于域隔离
中间件完成完整鉴权闭环
JWT 中间件不能只做“有没有 token”,要完成四步:解析 → 验证 → 绑定 → 拦截。
- 先检查 Authorization header 是否存在且格式为 Bearer xxxxx,不合法直接抛 DomainException,不交给 decode() 处理
- 调用 JWT::decode($token, $key, ['HS256']) 显式指定算法,防止默认算法列表引发兼容问题
- 验证通过后,把解码结果存到 $request->jwtUser(非 Session 或全局变量),控制器可直接读取
- 对需 RBAC 控制的接口,用 in_array('article:delete', $decoded->permissions) 判断,而非运行时查数据库
路由分组绑定 + 精准排除免鉴权路径
避免全局中间件误拦登录、刷新等必要接口。
立即学习“PHP免费学习笔记(深入)”;
- 不要在 app/middleware.php 全局注册 jwt.auth —— 它不支持 except 白名单
- 改用路由分组显式绑定:Route::group(['middleware' => 'jwt.auth'], function () { ... })->except(['api/v1/login', 'api/v1/refresh']);
- 注意 except 匹配的是完整 URL 路径(如 /api/v1/login),不是方法名或控制器名
- 若 API 绑定独立域名(如 api.example.com),需用 Route::domain('api.example.com', function () { ... }) 包裹全部路由,且 Web 服务器必须将该域名请求统一指向 public/index.php
接口级权限码必须落到具体动作
后端权限校验不能依赖前端按钮显隐,必须在进入控制器前完成。
- 权限码按规范命名,例如 user:update、order:pay、admin:dashboard,存入 auth_rule 表并加唯一索引
- 中间件中根据 $request->jwtUser->permissions 判断当前请求是否匹配该接口绑定的权限码
- 高并发场景下,用 Redis 缓存角色对应权限集合(如 redis key: role:admin:perms),O(1) 查询,权限变更时同步清理缓存
- 响应数据也需字段级过滤,例如普通用户请求 /api/v1/user 不应返回 is_admin 字段



















