在真实项目里,用 Yii2 构建安全、灵活、高可控的 OA Token 验证体系
2025-07-10
1️⃣ 背景与真实场景
项目是【信德 OA 对接推送系统】,需要支持外部系统(如钉钉、OA、ERP、外部审批流引擎)通过 HTTP 接口推送回调数据到我方平台。
核心需求:
每个合作伙伴需要分配独立的 Bearer Token 用于对接验证;
Token 可失效、可吊销、可监控、支持到期时间;
请求携带:
系统自动验证,无需复杂签名,但安全性要满足生产环境;
Authorization: Bearer xxxxx
不能破坏系统已有用户登录体系(已有
identityClass
已用于后台、用户系统认证);Token 必须可支持快速生成、列表查询、逻辑删除。
2️⃣ 一开始,我们如何生成 Bearer Token
初版快速生成:
使用:
$tokenRaw = base64_encode(random_bytes(32));
$bearerToken = "Bearer " . $tokenRaw;
存入
oa_partner_token_log
表,配合:'status' => 1,
'expire_time' => date('Y-m-d H
s', strtotime('+1 day')),
返回给对接方,粘贴到他们的服务器即可直接使用。
此方案简单,适合快速起步,但存在:
✅ 易用性好
❌ 无签名校验,可能被截获后伪造重放
❌ Token 内无法携带额外信息(如 tenant_id
, iat
)
3️⃣ 升级:引入加盐加密签名 Token
我们升级 Token 生成逻辑,保证:
✅ 抗伪造(加盐签名验证)
✅ Token 内携带 tenant_id
, iat
(签发时间)
✅ 避免 JWT 的复杂引入,同时兼顾可维护性
使用:
生成加密 Token:
$payload = json_encode([
'tid' => $tenantId,
'iat' => time(),
'rnd' => bin2hex(random_bytes(8)),
]);
$payloadBase64 = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));
$signature = hash_hmac('sha256', $payloadBase64, $secretKey);
$token = $payloadBase64 . '.' . $signature;
$bearerToken = "Bearer " . $token;
存入数据库,管理启用状态、失效时间。
4️⃣ 验证 Token 时如何做严格安全校验
我们使用以下校验流程:
$tokenParts = explode('.', $token);[$payloadBase64, $signature] = $tokenParts;$expectedSignature = hash_hmac('sha256', $payloadBase64, $secretKey);if (!hash_equals($expectedSignature, $signature)) { return null;}$payloadJson = base64_decode(str_replace(['-', '_'], ['+', '/'], $payloadBase64));$payload = json_decode($payloadJson, true);if (!$payload || !isset($payload['iat'])) { return null;}$bearerToken = "Bearer " . $token;return self::find() ->where(['token' => $bearerToken, 'status' => 1, 'is_deleted' => 0]) ->andWhere(['>', 'expire_time', date('Y-m-d H:i:s')]) ->one();
✅ 防止 Token 被伪造
✅ 验证结构完整性
✅ 验证有效期、状态、删除状态
5️⃣ 不破坏系统 identityClass 的 Bearer Token 验证方案
我们需要与现有用户认证体系并行使用,因此设计了:
自定义
OaHttpBearerAuth
继承AuthMethod
,模拟HttpBearerAuth
自动从 Header 获取 Token;内部直接调用上述严格验证方法;
验证通过后,将 Token Record 注入:
Yii::$app->params['oa_token_record'] = $tokenRecord;
在控制器中使用:
$tokenRecord = Yii::$app->params['oa_token_record'];
$tenantId = $tokenRecord->tenant_id;
✅ 不依赖 Yii::$app->user->identityClass
✅ 不影响现有用户体系
✅ 依然保留 Bearer Token
验证体验
6️⃣ 配套接口编写
✅ Token 列表接口
支持:
GET /oa-token/list?tenant_id=xx&status=1
返回对应列表数据便于管理查看。
✅ Token 删除接口(逻辑删除)
支持:
POST /oa-token/delete
id=xxx
内部:
$model = self::findOne(['id' => $id, 'is_deleted' => 0]);
$model->is_deleted = 1;
$model->save(false);
✅ 数据库字段调整
通过:
ALTER TABLE `oa_partner_config`ADD COLUMN `use_type` TINYINT NOT NULL DEFAULT 1 COMMENT '使用类型, 1-提审 2-其他' AFTER `status`;
在不破坏现有表结构的情况下增加管理字段。
7️⃣ 为什么这套方案稳定可靠
✅ 完全贴合 Yii2 的请求生命周期,利用过滤器自动校验,不需手写解析 header
✅ Token 可精细管控(启用、禁用、删除、可见性、有效期)
✅ Token 本身具备安全签名校验能力(SHA256 + Secret Key)
✅ Token 内携带关键信息(如 tenant_id
, iat
),便于未来做灰度控制、追踪溯源
✅ 不影响现有用户登录认证体系
✅ 可平滑扩展 JWT、OAuth2、速率限制、IP 白名单等能力
8️⃣ 可扩展与落地建议
如需继续落地,可在此基础上:
✅ 加入 Redis 缓存验证结果提升验证速度
✅ 扩展 Token 刷新、批量生成管理
✅ 接入灰度发布能力(根据 payload 中 tid
分流)
✅ 在数据推送时支持幂等去重(保证多次推送只处理一次)
🚀 结语
以上完整实战,是一次“从零到生产可用”的 高质量 Token 网关封装范例,也是在真实 OA 对接场景中,一步步做对安全性、易用性、可维护性、可扩展性平衡的过程。
如夜话,至此。
发表评论: