文档首页/ API网关 APIG/ 最佳实践/ API策略/ 使用APIG专享版的JWT认证策略实现身份认证和密钥轮转
更新时间:2025-10-31 GMT+08:00
分享

使用APIG专享版的JWT认证策略实现身份认证和密钥轮转

应用场景

JWT(JSON Web Token)是一种基于JSON的轻量级令牌,通过数字签名实现信息的安全传递,广泛应用于身份认证、信息交换等场景。

JWT常用于身份认证的令牌,利用无状态特性实现高效跨场景身份验证。用户通过认证服务器生成JWT,客户端通过在请求中携带Token来访问受保护的资源。网关收到请求后,验证JWT的有效性(签名、有效期等),并可以从Payload中直接提取用户身份和权限信息。

JWT的安全性依赖于签名密钥,密钥轮转(定期或按需更换签名密钥)是保障JWT安全的关键实践。长期使用同一密钥会增加泄露概率(如密钥被恶意窃取、内部人员滥用),定期轮转密钥可降低风险;此外,若发现密钥可能泄露,也可以立即触发应急轮转,终止旧密钥的有效性,防止攻击者利用泄露密钥伪造令牌。

方案优势

API网关的JWT认证策略支持从Header、Query、Cookie多种位置设置Token,通过校验Token实现身份认证。同时支持识别Payload中的claim并将身份信息提取出来,传递给后端。此外,用户还可以通过设置JWKS_URI远程服务地址,通过定期更换该地址返回的公钥,实现无缝密钥轮转。

约束与限制

  • 请求中携带的Token应当符合RFC 7519规范;JWT认证策略配置的公钥应当是符合RFC 7517规范的JSON格式字符串。
  • 由于JWT并不会对数据进行加密,请勿将敏感数据设置在Token中。此外为了避免Token泄露,建议您不要对请求协议为HTTP的API使用JWT认证
  • 网关会校验Token中的nbf(生效时间)和exp(过期时间)字段,如果校验失败会拒绝请求。
  • Token校验支持的加密算法包含RS256、RS384 、RS512、ES256、ES384和ES512。使用RSA算法时,建议密钥长度大于等于3072位
  • 当选择“定时拉取”的公钥设置方式时,必须保证JWKS_URI和APIG实例网络互通。网关内部的定时任务会每隔5min请求JWKS_URI,将返回的响应体作为公钥,并且当次请求的结果会覆盖上次请求的结果。如果要实现公私钥轮转,建议在每次轮换时,留出一段宽限时间,令JWKS_URI返回新公钥和本次轮转被替换的旧公钥,使得新旧私钥签发的Token在这段时间均有效。
  • 网关会根据Token和JWKS公钥中的kid进行匹配验签。如果JWKS中只存在一个JWK则kid可以为空,否则不可以为空;JWKS中任意两个JWK的kid不可以相同。如果未设置kid,则公私钥替换后,之前签发的Token无法校验通过。
  • 更多约束与限制,请参考配置API的JWT认证

操作流程

  1. 生成密钥对和签发Token

    通过在线生成或者本地生成密钥对和签发Token。

  2. 搭建远程JWKS服务地址

    搭建并维护一个返回JWK公钥的在线服务,供网关访问获取公钥。

  3. 创建JWT认证策略

    配置一个JWT认证策略,设置JWKS_URI。

  4. 绑定API

    将JWT认证策略绑定API。

  5. 验证身份认证是否生效

    通过改变请求携带的Token来测试API是否被JWT认证策略所保护。

  6. 实现密钥轮转

    通过更换远程JWKS服务返回的公钥实现密钥轮转。

  7. 验证密钥轮转是否生效

    验证新私钥签发的token是否能通过认证。

使用JWT认证策略实现身份认证和密钥轮转实施步骤

  1. 生成密钥对和签发Token。

    JWT的签发和验证依赖密钥对,您可以通过在线生成或者本地生成等方式来生成密钥对,并利用私钥签发Token。请您妥善保管私钥,避免泄露。
    • 在线生成
      1. 登录JWK密钥生成平台,“Key Size”选择“3072”,“Algorithm”选择“RS256”算法,输入自定义的Key ID,“Show X.509”选择“Yes”,单击“Generate”生成JWK及其对应的X.509格式的密钥。

      2. 登录JWT生成平台,“Algorithm”选择“RS256”算法,在header对应的json结构体中添加上一步自定义的kid(Key ID)字段,并在公钥和私钥位置处填入上一步生成的公钥和私钥,左边方框会自动生成对应的JWT。

    • 本地生成

      您可以利用JWT相关的开源代码仓,在本地运行代码来生成密钥对和签发Token。下方代码为生成密钥对和签发Token的Python代码示例。

      import jwt
      from cryptography.hazmat.primitives.asymmetric import rsa
      from cryptography.hazmat.primitives import serialization
      from jwcrypto.jwk import JWK
      import datetime
      private_key = rsa.generate_private_key(
          public_exponent=65537,
          key_size=3072
      )
      pem_private = private_key.private_bytes(
          encoding=serialization.Encoding.PEM,
          format=serialization.PrivateFormat.PKCS8,
          encryption_algorithm=serialization.NoEncryption()
      )
      public_key = private_key.public_key()
      pem_public = public_key.public_bytes(
          encoding=serialization.Encoding.PEM,
          format=serialization.PublicFormat.SubjectPublicKeyInfo
      )
      jwk_public = JWK.from_pem(pem_public)
      jwk_public_dict = jwk_public.export(as_dict=True)
      test_kid = "test"
      jwk_public_dict['kid'] = test_kid
      payload = {
          "sub": "1234567890",
          "name": "John Doe",
          "iat": datetime.datetime.utcnow(),
      }
      token = jwt.encode(
          payload,
          pem_private,
          algorithm="RS256",
          headers={"kid": test_kid}
      )
      print("======= Public Key (JWK Format) =======")
      print(jwk_public_dict)
      print("\n======= Private Key (PEM Format) =======")
      print(pem_private.decode('utf-8'))
      print("\n======= JWT =======")
      print(token)

  2. 搭建远程JWKS服务地址。

    根据1中生成的密钥对搭建和维护一个返回公钥的在线服务。API网关会每隔5min访问该服务来获取公钥,并将其缓存。此外,密钥轮转需要多个kid不同的公私密钥对。

    以下是返回两个JWK公钥的远程服务Python代码示例:

    from flask import Flask, request, abort, Response, jsonify, url_for
    import json
    import time
    app = Flask(__name__)
    @app.route("/jwks", methods=["GET"])
    def echo():
        return jsonify({
            "keys": [
                {
                    "kty": "RSA",
                    "e": "AQAB",
                    "kid": "test-kid-1",
                    "alg": "RS384",
                    "n": "oZaD8Tu7VKC1hnOvCa-DiouYKdHGaioKIWIu-vfvM0JHJdfFLOxJ4BVTksySZcWdv854_81hrYVpyIz_YjC8YHfQHmbtOjRQjcYHzQqoZTiZnS-NRjk4tjzYfOsc1F3oijZutxyeZctCgTn-gUyXIhXzKHsum-G4I0xWbBZzCGE7l0lMBHi6snrhwDz9eHwUSZviOYpKoYBf88FtBhHJTIt2_VLIrXRvwwP_joEMT56vKvX0dTpKE4HHMENWT4-p8IVyCJvtfPdZEg8hAgqfT4O0DHvfOpxAkSkVJpvJs3MA-VbYYRmZufM8TDI9jIyMffKIxxJEbzLgpBp-oy41mbOI-VSJpYnaBRRz1XV0GLeUIz_ri9Or8M29fGQb_hO1o5dLC5X06OzRZ--VENf53mnXwdUwesROexMF4_5JCJ7-Pefi0b6DTIQiPYd0IvKajzN1jwP4WfDzZE5E5FsX84gbkBn2G3aLgU2EPPoX7LPZTdlWMr5jF4FjT73HF0cr",
                },
                {
                    "kty": "RSA",
                    "e": "AQAB",
                    "kid": "test-kid-2",
                    "alg": "RS256",
                    "n": "lRv_mMn0hRIAlMjrcGnyFTIasr7rqwdK2GrQ5rNF76ZGrl-NVGpVTRq0IzcYzyOmLiFGfu1E-Tgs4aPwMyOVy1rXlXIlKYOShGblEIrtsFgd6b-xr09WKZcYTnnV16wH68WjpELDfFUNJ48GNU-c7co2UroQhUZ4Rh8dHIHl89-bayYMwBFMmVfVcimgF4xPut0weJdDm-bdU3RR1qJfjimnAyEA37qYynl7YTVGRBGM4kLnWn3sSJMDDd8v8AJMTHhWyi_DS5K7azkbQQMDd5hPKn_ylJ_-700N5fUqIWELSlj1L85qPdpQ62j109ShcFpVAXKvg64qGesxlLdzbgV6D3NWN_7wuGZS-exEi-gVJgMo-V1pNTMacRuooAK-VX6N-ds9nSMXb8P825XcFvGT1NecI5E7VcmqEvHjKcTEuGVWlTqLUjfM9szOC4wAHMfmFCXhiEAkfwq6kK39uM6hwKkUm_-HYUL_YbOWNRJ-hOtc7ooNMy4EXgDhLgK1"
                }
            ]
        }), 200
    if __name__ == '__main__':
        app.run(port=8080)

  3. 创建JWT认证策略。

    1. 登录API网关控制台页面,创建JWT认证策略。
    2. 在左侧导航栏中选择“API管理 > API策略”,单击“创建策略”,在弹窗中选择“JWT认证”。
    3. 公钥设置方式选择“定时拉取”,“JWKS_URI”填入搭建的JWKS服务地址,其余参数设置默认。请确保APIG实例与JWKS服务地址网络保持互通
    4. 单击“确定”,JWT认证策略创建成功。

  4. 绑定API。

    1. 单击已创建的策略名称,进入策略详情。
    2. 在“关联API”区域,单击“绑定API”,选择API分组、发布环境和需要绑定的API,单击“确定”。

  5. 验证身份认证是否生效。

    调用绑定JWT认证策略的API,如果请求携带了指定私钥生成的token,则请求通过JWT认证访问后端,否则返回认证失败。

  6. 实现密钥轮转。

    每次密钥轮转时,用户需要将远程JWKS服务地址返回的公钥更换为新的公钥,请求也需要携带新私钥签发的token来访问API。为了避免旧私钥签发的token在密钥轮转时立即失效导致认证失败,用户需要在密钥轮转后的一段“宽限时期”(根据旧私钥失效时间确定),设置远程JWKS服务同时返回旧公钥和新公钥。

    例如,如果上一个轮转周期内远程JWKS服务返回旧公钥的kid为“test-kid-1”,则密钥轮转时远程JWKS服务应当同时返回旧公钥(kid为“test-kid-1”)和新公钥(kid为“test-kid-2”),这样新旧私钥签发的token在这一段时间内均能通过JWT认证。当旧私钥签发的token均失效后,再让远程JWKS服务只返回新公钥(kid为“test-kid-2”)。

  7. 验证密钥轮转是否生效。

    根据6,调用绑定JWT认证策略的API,密钥轮转后,请求携带新私钥生成的token能通过JWT认证访问后端;此外,在“宽限时期”内,如果请求携带了旧私钥或者新私钥生成的token,均能通过JWT认证访问后端。

相关文档