推荐使用 webman/plugin-jwt 插件替代手写 jwt 逻辑,它轻量安全、自动拦截未授权请求;需配置强密钥、支持双通道鉴权、手动实现刷新与 redis 黑名单。

Webman 默认不带 JWT 支持,直接用 firebase/php-jwt 手写签发/验签逻辑是可行的,但容易漏掉关键校验点(比如 exp、nbf、iss 检查),也难统一拦截未授权请求。推荐用官方生态兼容的 webman/plugin-jwt 插件——它轻量、无侵入、自动处理中间件拦截和异常响应,比自己封装更省心也更安全。
安装 webman/plugin-jwt 并配置密钥
插件本身不生成密钥,你得自己准备一个强随机 secret,并通过配置注入。别用硬编码字符串,也别用 md5('123') 这类弱值。
- 执行
composer require webman/plugin-jwt - 在
config/plugin/webman/plugin-jwt/app.php中填入你的密钥(建议 32 字节以上):'secret' => $_ENV['JWT_SECRET'] ?? 'your_32_byte_strong_secret_here',
- 把密钥写进
.env文件:JWT_SECRET=8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d
- 插件默认使用
HS256算法,无需额外配置;如需 RSA,得改algorithm并加载 PEM 密钥文件
登录接口返回 token 要带 HTTP-only 安全头
Webman 是长连接模型,但 JWT 仍应走标准 Authorization 头。很多教程让前端存 localStorage,这是 XSS 高危操作。更稳妥的做法是:后端登录成功后,把 token 放在响应体里,同时设置 Set-Cookie(HTTP-only + Secure + SameSite=Strict),由浏览器自动携带。
- 登录控制器中调用
jwt()->encode()生成 token,例如:$token = jwt()->encode(['user_id' => $user['id'], 'exp' => time() + 3600]);
- 手动添加 Cookie(Webman 不自动设):
response()->withCookie('token', $token, 3600, '/', '', true, true, false, 'Strict'); - 别忘了响应体也要返回:
return json(['token' => $token]);
——供移动端或调试用 - 注意:若前端是 Vue/React 单页应用且跨域,Cookie 方案需配合
credentials: 'include'和后端 CORS 允许凭据
中间件自动验证 Authorization 或 Cookie 中的 token
插件默认只检查 Authorization: Bearer xxx,不读 Cookie。要支持双通道(API 调用走 Header,Web 页面走 Cookie),得自定义中间件继承 Plugin\Jwt\Middleware\JwtAuth,重写 getToken() 方法。
- 新建
app/middleware/JwtAuthDual.php:public function getToken($request) { $token = $request->header('authorization', ''); if (strpos($token, 'Bearer ') === 0) { return trim(substr($token, 7)); } return $request->cookie('token', ''); } - 在路由或全局中间件中注册它,而不是原生
Plugin\Jwt\Middleware\JwtAuth - 验证失败时,插件抛
Plugin\Jwt\Exception\TokenInvalidException,你可在app/exception/Handler.php中捕获并统一返回 401 - 注意:
exp和nbf校验默认开启,但iss、aud需手动在app.php配置项里补全,否则不校验
刷新 token 和黑名单必须自己实现
插件不提供 refresh endpoint 或黑名单存储,因为 Webman 本身无内置缓存/DB 抽象层。这部分逻辑逃不掉手写,且最容易出错。
- 刷新接口不能只 decode 再 encode —— 必须检查原 token 的
jti是否在黑名单里,再生成新 token 并记录新jti到 Redis - 黑名单 key 建议用
jwt:blacklist:+jti,过期时间设为原 token 剩余有效期 + 缓冲(如 +300 秒) - 登出时,取当前 token 的
jti(jwt()->decode($token)->jti)并写入黑名单;不要删 Cookie,而是让下一次请求因黑名单被拒 - 别用 MySQL 存黑名单——高并发下会成瓶颈;Redis 的
SET+EXPIRE是合理选择
JWT 的“无状态”是假定服务端不存会话,但黑名单、刷新、设备绑定这些增强能力,必然引入状态。想完全零状态,就得接受无法主动作废 token 的代价。这点在设计初期就得明确取舍。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











