无尘阁日记

无尘阁日记

在真实项目里,用 Yii2 构建安全、灵活、高可控的 OA Token 验证体系
2025-07-10

1️⃣ 背景与真实场景

项目是【信德 OA 对接推送系统】,需要支持外部系统(如钉钉、OA、ERP、外部审批流引擎)通过 HTTP 接口推送回调数据到我方平台。

核心需求:

  • 每个合作伙伴需要分配独立的 Bearer Token 用于对接验证;

  • Token 可失效、可吊销、可监控、支持到期时间;

  • 请求携带:


    系统自动验证,无需复杂签名,但安全性要满足生产环境;

    1. Authorization: Bearer xxxxx

  • 不能破坏系统已有用户登录体系(已有 identityClass 已用于后台、用户系统认证);

  • Token 必须可支持快速生成、列表查询、逻辑删除。

2️⃣ 一开始,我们如何生成 Bearer Token

初版快速生成:

  • 使用:


    1. $tokenRaw = base64_encode(random_bytes(32));

    2. $bearerToken = "Bearer " . $tokenRaw;

  • 存入 oa_partner_token_log 表,配合:


    1. 'status' => 1,

    2. 'expire_time' => date('Y-m-d H:i:s', strtotime('+1 day')),

  • 返回给对接方,粘贴到他们的服务器即可直接使用。

此方案简单,适合快速起步,但存在:
✅ 易用性好
❌ 无签名校验,可能被截获后伪造重放
❌ Token 内无法携带额外信息(如 tenant_idiat

3️⃣ 升级:引入加盐加密签名 Token

我们升级 Token 生成逻辑,保证:
✅ 抗伪造(加盐签名验证)
✅ Token 内携带 tenant_idiat(签发时间)
✅ 避免 JWT 的复杂引入,同时兼顾可维护性

使用:

  • 生成加密 Token:


    1. $payload = json_encode([

    2.    'tid' => $tenantId,

    3.    'iat' => time(),

    4.    'rnd' => bin2hex(random_bytes(8)),

    5. ]);

    6. $payloadBase64 = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));

    7. $signature = hash_hmac('sha256', $payloadBase64, $secretKey);

    8. $token = $payloadBase64 . '.' . $signature;

    9. $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 注入:


    1. Yii::$app->params['oa_token_record'] = $tokenRecord;

  • 在控制器中使用:


    1. $tokenRecord = Yii::$app->params['oa_token_record'];

    2. $tenantId = $tokenRecord->tenant_id;

✅ 不依赖 Yii::$app->user->identityClass
✅ 不影响现有用户体系
✅ 依然保留 Bearer Token 验证体验

6️⃣ 配套接口编写

✅ Token 列表接口

  • 支持:


    1. GET /oa-token/list?tenant_id=xx&status=1

  • 返回对应列表数据便于管理查看。

✅ Token 删除接口(逻辑删除)

  • 支持:


    1. POST /oa-token/delete

    2. id=xxx

  • 内部:


    1. $model = self::findOne(['id' => $id, 'is_deleted' => 0]);

    2. $model->is_deleted = 1;

    3. $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_idiat),便于未来做灰度控制、追踪溯源
✅ 不影响现有用户登录认证体系
✅ 可平滑扩展 JWT、OAuth2、速率限制、IP 白名单等能力

8️⃣ 可扩展与落地建议

如需继续落地,可在此基础上:
✅ 加入 Redis 缓存验证结果提升验证速度
✅ 扩展 Token 刷新、批量生成管理
✅ 接入灰度发布能力(根据 payload 中 tid 分流)
✅ 在数据推送时支持幂等去重(保证多次推送只处理一次)

🚀 结语

以上完整实战,是一次“从零到生产可用”的 高质量 Token 网关封装范例,也是在真实 OA 对接场景中,一步步做对安全性、易用性、可维护性、可扩展性平衡的过程。

如夜话,至此。