目录
Please enable Javascript to view the contents

License 微服务授权设计:私钥签发服务证书

 ·  ☕ 5 分钟

重要

License 授权的核心设计:服务端持有私钥签发 License 证书,业务服务随部署导入证书并使用内置公钥验签,从而实现离线、可审计、低侵入的授权控制。

环境说明

  • 语言:Go
  • 场景:私有化部署 / 内网部署 / 微服务授权
  • 方式:非对称签名 + 服务启动校验 + 周期性校验

1.简介

私有化交付场景下,服务通常部署在客户内网,不能依赖公网授权中心实时校验。

因此 License 设计需要满足:

目标说明
离线可用客户环境无公网时仍可完成授权校验
不暴露私钥私钥只在签发侧保存,不进入业务服务、不进入客户环境
部署简单License 文件跟随服务部署导入
可限制范围支持有效期、模块、节点数、租户、环境等约束
可审计授权内容可解析、可验签、可追溯
核心原则:签发权在服务端,校验权在业务服务;私钥不下发,公钥可内置。

2.整体设计

2.1 角色划分

角色持有内容职责
License 签发服务私钥生成授权内容并签名
业务服务公钥导入 License 后验签并读取授权范围
部署系统License 文件随服务部署分发授权文件
运维人员授权申请信息申请、替换、续期 License

2.2 授权流程

授权申请信息
    ↓
License 签发服务
    ↓ 使用私钥签名
License 文件 / License 证书
    ↓ 随业务服务部署导入
业务服务启动
    ↓ 使用内置公钥验签
读取授权范围
    ↓
启用对应功能模块

3.说明

3.1 License 文件内容

License 文件建议分为两部分:

部分内容是否敏感
Payload授权主体、有效期、模块列表、部署规模等否,但需要避免泄露客户信息
Signature使用私钥对 Payload 签名后的结果否,不能反推出私钥

示例结构如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "payload": {
    "subject": "customer-xxx",
    "product": "platform-service",
    "modules": ["module-a", "module-b"],
    "maxNodes": 100,
    "issuedAt": "2024-01-01T00:00:00Z",
    "expiresAt": "2025-01-01T00:00:00Z"
  },
  "signature": "base64-signature"
}

需要注意的是,subjectproductmodules 在公开文章中必须脱敏。真实项目中可以使用客户编号、产品编码、模块编码,不建议直接写客户名称或内部服务名。

3.2 签发逻辑

签发服务只做两件事:

  1. 生成标准化 Payload;
  2. 使用私钥对 Payload 做签名。

伪代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func IssueLicense(payload Payload, privateKey PrivateKey) (License, error) {
    data, err := canonicalJSON(payload)
    if err != nil {
        return License{}, err
    }

    signature, err := sign(data, privateKey)
    if err != nil {
        return License{}, err
    }

    return License{
        Payload:   payload,
        Signature: base64.StdEncoding.EncodeToString(signature),
    }, nil
}

关键点:

说明
Payload 必须规范化避免 JSON 字段顺序、空格、编码差异导致验签失败
私钥必须独立保存不进入镜像、不进入 Git、不进入客户环境
签发记录需要留痕便于授权追踪、续期、吊销

3.3 服务侧校验逻辑

业务服务启动时读取 License 文件,使用内置公钥验签。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
func VerifyLicense(license License, publicKey PublicKey) error {
    data, err := canonicalJSON(license.Payload)
    if err != nil {
        return err
    }

    signature, err := base64.StdEncoding.DecodeString(license.Signature)
    if err != nil {
        return err
    }

    if err := verify(data, signature, publicKey); err != nil {
        return ErrInvalidSignature
    }

    if time.Now().After(license.Payload.ExpiresAt) {
        return ErrLicenseExpired
    }

    return nil
}

建议校验点:

校验项说明
签名校验确认 License 未被篡改,且确实由私钥持有方签发
有效期到期后拒绝启动或降级运行
模块权限未授权模块不加载或不可调用
部署规模校验节点数、实例数、租户数等限制
环境绑定可选,绑定机器码、集群 ID、租户 ID 等

3.4 为什么不用对称加密

方案优点问题
对称密钥实现简单业务服务必须持有同一密钥,密钥泄露后可伪造 License
非对称签名私钥不下发,业务侧只需要公钥实现略复杂,但安全边界清晰

License 授权推荐使用非对称签名。业务服务只负责验签,不具备签发能力。

3.5 私钥管理

私钥是整套 License 体系的根权限。

不建议:

  • 私钥写入代码仓库;
  • 私钥打进业务镜像;
  • 私钥通过配置文件下发到客户环境;
  • 所有环境共用同一把私钥且无轮换机制。

建议:

建议
存储放在独立签发服务或 KMS/HSM 中
权限只允许授权签发流程访问
审计记录签发人、签发时间、授权范围
轮换支持多公钥验签,平滑切换新旧密钥

3.6 部署集成

License 文件可以跟随服务部署导入,常见方式:

部署方式License 挂载方式
二进制部署放在指定配置目录
Docker通过 volume 挂载
Kubernetes使用 Secret / ConfigMap 挂载

Kubernetes 示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apiVersion: v1
kind: Secret
metadata:
  name: license-secret
type: Opaque
stringData:
  license.json: |
    {
      "payload": {},
      "signature": "base64-signature"
    }    
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-service
spec:
  template:
    spec:
      containers:
        - name: app-service
          image: app-service:latest
          volumeMounts:
            - name: license
              mountPath: /etc/app/license
              readOnly: true
      volumes:
        - name: license
          secret:
            secretName: license-secret

需要注意的是,License 本身不等同于私钥。License 可以随部署分发,私钥不能。

4.常见问题

4.1 License 被复制怎么办

如果只校验签名和有效期,License 文件可以被复制到其他环境使用。

解决方案:

方案说明
绑定机器码适合物理机或固定虚拟机部署
绑定集群 ID适合 Kubernetes 私有化部署
绑定租户 ID适合多租户平台
绑定节点规模限制最大节点数或实例数

是否绑定环境,需要结合交付方式决定。绑定越强,运维复杂度越高。

4.2 到期后如何处理

常见处理方式:

策略行为适用场景
拒绝启动服务启动失败强授权控制
降级运行核心功能可用,高级模块禁用避免生产事故
只告警不影响运行试用期、灰度阶段

生产环境不建议到期后直接导致核心链路不可用,除非合同和交付流程明确允许。

4.3 如何支持续期

续期本质是重新签发一份新 License:

  1. 读取原授权主体;
  2. 调整 expiresAt 或授权范围;
  3. 使用私钥重新签名;
  4. 替换部署侧 License 文件;
  5. 服务重启或热加载。

如果需要热加载,需要监听 License 文件变化,并重新执行验签流程。

5.总结

License 微服务授权的核心不是加密,而是签名与验签的权限隔离

设计点结论
私钥只在签发服务侧保存
公钥内置到业务服务,用于验签
License随部署导入,描述授权范围
校验启动时校验,运行中可周期性校验
安全边界业务服务只能验证,不能签发

一句话说明:License 签发服务使用私钥签发服务证书,业务服务随部署导入 License,并通过内置公钥验签授权范围。

6.参考

  • RSA / ECDSA 非对称签名
  • X.509 证书体系
  • Kubernetes Secret 挂载配置
  • 私有化部署授权设计
分享

Hex
作者
Hex
CloudNative Developer