jwt-auth
jwt-auth 插件支持使用 JSON Web Token (JWT) 作为机制,在客户端访问上游资源之前验证其身份。
启用后,JWT 凭据会配置在 消费者 上,客户端携带签名后的令牌向 APISIX 标识自 己。令牌可以包含在请求 URL 查询字符串、请求头或 Cookie 中。APISIX 随后将验证令牌,以决定允许或拒绝请求访问上游资源。
消费者成功通过身份认证后,APISIX 会在将请求代理到上游服务之前添加额外的请求头,例如 X-Consumer-Username、X-Credential-Identifier 以及配置的其他消费者自定义请求头。上游服务可以据此区分消费者并按需实现额外逻辑。如果这些值不可用,则不会添加相应的请求头。
使用 Ingress Controller 配置消费者时,消费者名称会生成为 namespace_consumername 格式。因此,X-Consumer-Username 请求头也会采用此格式,而不只是 consumername。
示例
以下示例演示了如何在不同场景下使用 jwt-auth 插件。
使用 JWT 进行消费者认证
以下示例演示了如何实现基于 JWT 的消费者密钥认证。
- Admin API
- ADC
- Ingress Controller
创建消费者 jack:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "jack"
}'
为消费者创建 jwt-auth 凭据:
curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-jack-jwt-auth",
"plugins": {
"jwt-auth": {
"key": "jack-key",
"secret": "jack-hs256-secret-that-is-very-long"
}
}
}'
创建一个启用 jwt-auth 插件的路由:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "jwt-route",
"uri": "/headers",
"plugins": {
"jwt-auth": {}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
创建一个配置了 jwt-auth 凭证的消费者,以及一个配置了 jwt-auth 插件的路由:
consumers:
- username: jack
credentials:
- name: jwt-auth
type: jwt-auth
config:
key: jack-key
secret: jack-hs256-secret-that-is-very-long
services:
- name: jwt-auth-service
routes:
- name: jwt-route
uris:
- /headers
plugins:
jwt-auth: {}
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
ADC 会将选定的顶层资源类型作为期望状态进行协调。标签选择器将此示例限制在其自身带标签的服务和消费者内;凭证仍嵌套在所属消费者下。请先预览限定范围的变更,并确认其中没有意外更新或删除:
adc diff -f adc.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=jwt-auth
同步已审查的配置:
adc sync -f adc.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=jwt-auth
- Gateway API
- APISIX CRD
创建一个配置了 jwt-auth 凭证的消费者,以及一个配置了 jwt-auth 插件的路由:
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: aic
name: jack
spec:
gatewayRef:
name: apisix
credentials:
- type: jwt-auth
name: primary-cred
config:
key: jack-key
secret: jack-hs256-secret-that-is-very-long
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: jwt-auth-plugin-config
spec:
plugins:
- name: jwt-auth
config:
_meta:
disable: false
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: jwt-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /headers
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: jwt-auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
将配置应用到集群:
kubectl apply -f jwt-auth-ic.yaml
创建一个使用 HS256 的 jwt-auth 凭证消费者,并按如下方式创建一个启用了 jwt-auth 插件的路由:
apiVersion: apisix.apache.org/v2
kind: ApisixConsumer
metadata:
namespace: aic
name: jack
spec:
ingressClassName: apisix
authParameter:
jwtAuth:
value:
key: jack-key
secret: jack-hs256-secret-that-is-very-long
---
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: jwt-route
spec:
ingressClassName: apisix
http:
- name: jwt-route
match:
paths:
- /headers
upstreams:
- name: httpbin-external-domain
plugins:
- name: jwt-auth
enable: true
config:
_meta:
disable: false
将配置应用到集群:
kubectl apply -f jwt-auth-ic.yaml
要为 jack 签发 JWT,你可以使用 JWT.io 的 JWT 编码器 或其他工具。如果你使用的是 JWT.io 的 JWT 编码器,请执行以下操作:
- 在算法栏填写
HS256。 - 在 Valid secret 部分将密钥更新为
jack-hs256-secret-that-is-very-long。 - 使用消费者密钥
jack-key更新 payload;并添加 UNIX 时间戳格式的exp或nbf。
当 claims_to_verify 为非空列表时,列表中的每个 Claim 都必须存在并通过验证。未设置或设置为空列表时,如果 exp 和 nbf 存在,则会对其进行验证,但不会强制要求这两个 Claim 存在。
你的 payload 应该类似于以下内容:
{
"key": "jack-key",
"nbf": 1729132271
}
复制生成的 JWT 并保存到变量中:
export jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU
向路由发送在 Authorization 请求头中携带 JWT 的请求:
curl -i "http://127.0.0.1:9080/headers" -H "Authorization: ${jwt_token}"
你应该收到类似于以下的 HTTP/1.1 200 OK 响应:
{
"headers": {
"Accept": "*/*",
"Authorization": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MjY2NDk2NDAsImtleSI6ImphY2sta2V5In0.kdhumNWrZFxjUvYzWLt4lFr546PNsr9TXuf0Az5opoM",
"Host": "127.0.0.1",
"User-Agent": "curl/8.6.0",
"X-Amzn-Trace-Id": "Root=1-66ea951a-4d740d724bd2a44f174d4daf",
"X-Consumer-Username": "jack",
"X-Credential-Identifier": "cred-jack-jwt-auth",
"X-Forwarded-Host": "127.0.0.1"
}
}
发送带有无效令牌的请求:
curl -i "http://127.0.0.1:9080/headers" -H "Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MjY2NDk2NDAsImtleSI6ImphY2sta2V5In0.kdhumNWrZFxjU_random_random"
你应该收到类似于以下的 HTTP/1.1 401 Unauthorized 响应:
{"message":"failed to verify jwt"}
在请求头、查询字符串或 Cookie 中携带 JWT
以下示例演示了如何接受指定请求头、查询字符串和 Cookie 中的 JWT。
- Admin API
- ADC
- Ingress Controller
创建消费者 jack:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "jack"
}'
为消费者创建 jwt-auth 凭据:
curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-jack-jwt-auth",
"plugins": {
"jwt-auth": {
"key": "jack-key",
"secret": "jack-hs256-secret-that-is-very-long"
}
}
}'
创建一个启用 jwt-auth 插件的路由,并指定携带令牌的请求参数:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "jwt-route",
"uri": "/get",
"plugins": {
"jwt-auth": {
"header": "jwt-auth-header",
"query": "jwt-query",
"cookie": "jwt-cookie"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
创建一个配置了 jwt-auth 凭证的消费者,以及一个配置了 jwt-auth 插件的路由:
consumers:
- username: jack
credentials:
- name: jwt-auth
type: jwt-auth
config:
key: jack-key
secret: jack-hs256-secret-that-is-very-long
services:
- name: jwt-auth-service
routes:
- name: jwt-route
uris:
- /get
plugins:
jwt-auth:
header: jwt-auth-header
query: jwt-query
cookie: jwt-cookie
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
ADC 会将选定的顶层资源类型作为期望状态进行协调。标签选择器将此示例限制在其自身带标签的服务和消费者内;凭证仍嵌套在所属消费者下。请先预览限定范围的变更,并确认其中没有意外更新或删除:
adc diff -f adc.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=jwt-auth
同步已审查的配置:
adc sync -f adc.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=jwt-auth
- Gateway API
- APISIX CRD
创建一个配置了 jwt-auth 凭证的消费者,以及一个配置了 jwt-auth 插件的路由:
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: aic
name: jack
spec:
gatewayRef:
name: apisix
credentials:
- type: jwt-auth
name: primary-cred
config:
key: jack-key
secret: jack-hs256-secret-that-is-very-long
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: jwt-auth-plugin-config
spec:
plugins:
- name: jwt-auth
config:
_meta:
disable: false
header: jwt-auth-header
query: jwt-query
cookie: jwt-cookie
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: jwt-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /get
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: jwt-auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
将配置应用到集群:
kubectl apply -f jwt-auth-ic.yaml
创建一个配置了 jwt-auth 凭证的消费者,以及一个配置了 jwt-auth 插件的路由:
apiVersion: apisix.apache.org/v2
kind: ApisixConsumer
metadata:
namespace: aic
name: jack
spec:
ingressClassName: apisix
authParameter:
jwtAuth:
value:
key: jack-key
secret: jack-hs256-secret-that-is-very-long
---
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: jwt-route
spec:
ingressClassName: apisix
http:
- name: jwt-route
match:
paths:
- /get
upstreams:
- name: httpbin-external-domain
plugins:
- name: jwt-auth
enable: true
config:
header: jwt-auth-header
query: jwt-query
cookie: jwt-cookie
将配置应用到集群:
kubectl apply -f jwt-auth-ic.yaml
要为 jack 签发 JWT,你可以使用 JWT.io 的 JWT 编码器 或其他工具。如果你使用的是 JWT.io 的 JWT 编码器,请执行以下操作:
- 在算法栏填写
HS256。 - 在 Valid secret 部分将密钥更新为
jack-hs256-secret-that-is-very-long。 - 使用消费者密钥
jack-key更新 payload;并添加 UNIX 时间戳格式的exp或nbf。
当 claims_to_verify 为非空列表时,列表中的每个 Claim 都必须存在并通过验证。未设置或设置为空列表时,如果 exp 和 nbf 存在,则会对其进行验证,但不会强制要求这两个 Claim 存在。
你的 payload 应该类似于以下内容:
{
"key": "jack-key",
"nbf": 1729132271
}
复制生成的 JWT 并保存到变量中:
export jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU
在请求头中验证 JWT
发送带有请求头 JWT 的请求:
curl -i "http://127.0.0.1:9080/get" -H "jwt-auth-header: ${jwt_token}"
你应该收到类似于以下的 HTTP/1.1 200 OK 响应:
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "127.0.0.1",
"Jwt-Auth-Header": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU",
...
},
...
}
在查询字符串中验证 JWT
发送带有查询字符串 JWT 的请求:
curl -i "http://127.0.0.1:9080/get?jwt-query=${jwt_token}"
你应该收到类似于以下的 HTTP/1.1 200 OK 响应:
{
"args": {
"jwt-query": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU"
},
"headers": {
"Accept": "*/*",
...
},
"origin": "127.0.0.1, 183.17.233.107",
"url": "http://127.0.0.1/get?jwt-query=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTY5NTEyOTA0NH0.EiktFX7di_tBbspbjmqDKoWAD9JG39Wo_CAQ1LZ9voQ"
}
在 Cookie 中验证 JWT
发送带有 Cookie JWT 的请求:
curl -i "http://127.0.0.1:9080/get" --cookie jwt-cookie=${jwt_token}
你应该收到类似于以下的 HTTP/1.1 200 OK 响应:
{
"args": {},
"headers": {
"Accept": "*/*",
"Cookie": "jwt-cookie=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU",
...
},
...
}
在环境变量中管理密钥
以下示例演示了如何将 jwt-auth 消费者密钥保存到环境变量并在配置中引用它。
APISIX 支持引用通过 NGINX env 指令 配置的系统和用户环境变量。
将密钥保存到环境变量:
export JACK_JWT_SECRET=jack-hs256-secret-that-is-very-long
如果你在 Docker 中运行 APISIX,你应该在启动容器时使用 -e 标志设置环境变量。
- Admin API
- ADC
创建消费者 jack:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "jack"
}'
为消费者创建 jwt-auth 凭据并引用环境变量:
curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-jack-jwt-auth",
"plugins": {
"jwt-auth": {
"key": "jack-key",
"secret": "$env://JACK_JWT_SECRET"
}
}
}'
创建一个启用 jwt-auth 的路由:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "jwt-route",
"uri": "/get",
"plugins": {
"jwt-auth": {}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
创建一个通过环境变量引用 jwt-auth 凭证的消费者,并按如下方式创建一个启用了 jwt-auth 插件的路由:
consumers:
- username: jack
credentials:
- name: jwt-auth
type: jwt-auth
config:
key: jack-key
secret: $env://JACK_JWT_SECRET
services:
- name: jwt-auth-service
routes:
- name: jwt-route
uris:
- /get
plugins:
jwt-auth: {}
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
ADC 会将选定的顶层资源类型作为期望状态进行协调。标签选择器将此示例限制在其自身带标签的服务和消费者内;凭证仍嵌套在所属消费者下。请先预览限定范围的变更,并确认其中没有意外更新或删除:
adc diff -f adc.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=jwt-auth
同步已审查的配置:
adc sync -f adc.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=jwt-auth
要为 jack 签发 JWT,你可以使用 JWT.io 的 JWT 编码器 或其他工具。如果你使用的是 JWT.io 的 JWT 编码器,请执行以下操作:
- 在算法栏填写
HS256。 - 在 Valid secret 部分将密钥更新为
jack-hs256-secret-that-is-very-long。 - 使用消费者密钥
jack-key更新 payload;并添加 UNIX 时间戳格式的exp或nbf。
当 claims_to_verify 为非空列表时,列表中的每个 Claim 都必须存在并通过验证。未设置或设置为空列表时,如果 exp 和 nbf 存在,则会对其进行验证,但不会强制要求这两个 Claim 存在。
你的 payload 应该类似于以下内容:
{
"key": "jack-key",
"nbf": 1729132271
}
复制生成的 JWT 并保存到变量中:
export jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU
发送带有请求头 JWT 的请求:
curl -i "http://127.0.0.1:9080/get" -H "Authorization: ${jwt_token}"
你应该收到 HTTP/1.1 200 OK 响应。
将 JWT Secret 存储在 HashiCorp Vault 中
HashiCorp Vault 为 Secret 提供集中式外部存储。以下示例将 JWT 签名 Secret 存储在 Vault 中,而不是直接写入 jwt-auth 凭据,并从网关引用该 Secret。
Vault 开发模式将数据存储在内存中,并使用 Root Token。此设置仅用于本地测试。在生产环境中,请运行生产级 Vault 部署,并仅向网关授予读取所需 Secret 路径的 Token。
将 GATEWAY_CONTAINER 设置为正在运行的 APISIX 或 API7 网关容器名称。创建专用 Docker 网络并将网关连接到该网络:
export GATEWAY_CONTAINER=replace-with-gateway-container-name
docker network create gateway-vault-net
docker network connect gateway-vault-net "$GATEWAY_CONTAINER"
在同一网络中启动 Vault 开发服务器:
docker run -d \
--name gateway-vault \
--network gateway-vault-net \
--cap-add IPC_LOCK \
-e VAULT_DEV_ROOT_TOKEN_ID=root \
-e VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200 \
hashicorp/vault:1.21.4 \
vault server -dev
网关的 Vault Secret Provider 从 Vault KV 版本 1读取数据。在 kv/ 路径启用 KV 版本 1 Secret 引擎:
docker exec gateway-vault sh -c \
"VAULT_TOKEN='root' VAULT_ADDR='http://127.0.0.1:8200' vault secrets enable -path=kv -version=1 kv"
你应该看到类似于以下的响应:
Success! Enabled the kv secrets engine at: kv/
将签名 Secret 存储在 kv/apisix/jack:
docker exec gateway-vault sh -c \
"VAULT_TOKEN='root' VAULT_ADDR='http://127.0.0.1:8200' vault kv put kv/apisix/jack jwt-secret=vault-hs256-secret-that-is-very-long"
你应该看到类似于以下的响应:
Success! Data written to: kv/apisix/jack
创建包含 Vault 连接信息的网关 Secret 资源。目前 ADC 不会同步 Secret 资源,因此请通过 Admin API 创建该资源:
curl "http://127.0.0.1:9180/apisix/admin/secrets/vault/jwt" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "http://gateway-vault:8200",
"prefix": "kv/apisix",
"token": "root"
}'
由于 Vault 容器与网关运行在同一 Docker 网络中,因此网关可以通过容器名称访问 Vault。
以下两种配置方式都需要该 Secret 资源。目前 ADC 不会同步 Secret 资源,但可以同步引用已有 Secret 资源的凭据。
- Admin API
- ADC
创建消费者 jack:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "jack"
}'
为消费者创建 jwt-auth 凭据并引用该 Secret:
curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-jack-jwt-auth",
"plugins": {
"jwt-auth": {
"key": "jwt-vault-key",
"secret": "$secret://vault/jwt/jack/jwt-secret"
}
}
}'
创建一个启用 jwt-auth 的路由:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "jwt-route",
"uri": "/get",
"plugins": {
"jwt-auth": {}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
创建包含消费者、由 Vault 支持的凭据和路由的 adc.yaml:
consumers:
- username: jack
credentials:
- id: cred-jack-jwt-auth
name: jwt-auth
type: jwt-auth
config:
key: jwt-vault-key
secret: $secret://vault/jwt/jack/jwt-secret
services:
- name: jwt-auth-service
routes:
- name: jwt-route
uris:
- /get
plugins:
jwt-auth: {}
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
ADC 会将选定的顶层资源类型作为期望状态进行协调。标签选择器将此示例限制在其自身带标签的服务和消费者内;凭证仍嵌套在所属消费者下。请先预览限定范围的变更,并确认其中没有意外更新或删除:
adc diff -f adc.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=jwt-auth
同步已审查的配置:
adc sync -f adc.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=jwt-auth
在这两种方式中,引用都会使用 jwt Secret 资源读取 kv/apisix/jack 中的 jwt-secret 字段。
使用兼容的工具生成 JWT。如果使用 JWT.io 的 JWT 编码器,请按如下方式配置 Token:
- 选择
HS256算法。 - 将 Valid secret 设置为
vault-hs256-secret-that-is-very-long。 - 将
keyClaim 设置为jwt-vault-key,并添加使用有效 Unix 时间戳的exp或nbfClaim。
当 claims_to_verify 为非空列表时,列表中的每个 Claim 都必须存在并通过验证。未设置或设置为空列表时,如果 exp 和 nbf 存在,则会对其进行验证,但不会强制要求这两个 Claim 存在。
你的 payload 应该类似于以下内容:
{
"key": "jwt-vault-key",
"nbf": 1729132271
}
复制生成的 JWT 并保存到变量中:
export jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqd3QtdmF1bHQta2V5IiwibmJmIjoxNzI5MTMyMjcxfQ.i2pLj7QcQvnlSjB7iV5V522tIV43boQRtee7L0rwlkQ
发送带有请求头令牌的请求:
curl -i "http://127.0.0.1:9080/get" -H "Authorization: ${jwt_token}"
你应该收到 HTTP/1.1 200 OK 响应。响应体应包含用于标识已认证消费者的请求头:
{
"headers": {
"X-Consumer-Username": "jack",
"X-Credential-Identifier": "cred-jack-jwt-auth"
}
}
使用 RS256 算法签署 JWT
以下示例演示了在实施 JWT 进行消费者身份认证时,如何使用非对称算法(如 RS256)来签署和验证 JWT。你将使用 openssl 生成 RSA 密钥对,并使用 JWT.io 生成 JWT,以更好地理解 JWT 的组成。
生成 2048 位 RSA 私钥并提取 PEM 格式的对应公钥:
openssl genrsa -out jwt-rsa256-private.pem 2048
openssl rsa -in jwt-rsa256-private.pem -pubout -out jwt-rsa256-public.pem
你应该在当前工作目录中看到生成的 jwt-rsa256-private.pem 和 jwt-rsa256-public.pem。
访问 JWT.io 的 JWT 编码器 并执行以下操作:
- 在算法栏填写
RS256。 - 将私钥内容复制并粘贴到 SIGN JWT: PRIVATE KEY 部分。
- 使用消费者密钥
jack-key更新 payload;并添加 UNIX 时间戳格式的exp或nbf。
你的 payload 应该类似于以下内容:
{
"key": "jack-key",
"nbf": 1729132271
}
当 claims_to_verify 为非空列表时,列表中的每个 Claim 都必须存在并通过验证。未设置或设置为空列表时,如果 exp 和 nbf 存在,则会对其进行验证,但不会强制要求这两个 Claim 存在。
复制生成的 JWT 并保存到变量中:
export jwt_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.K-I13em84kAcyH1jfIJl7ls_4jlwg1GzEzo5_xrDu-3wt3Xa3irS6naUsWpxX-a-hmcZZxRa9zqunqQjUP4kvn5e3xg2f_KyCR-_ZbwqYEPk3bXeFV1l4iypv6z5L7W1Niharun-dpMU03b1Tz64vhFx6UwxNL5UIZ7bunDAo_BXZ7Xe8rFhNHvIHyBFsDEXIBgx8lNYMq8QJk3iKxZhZZ5Om7lgYjOOKRgew4WkhBAY0v1AkO77nTlvSK0OEeeiwhkROyntggyx-S-U222ykMQ6mBLxkP4Cq5qHwXD8AUcLk5mhEij-3QhboYnt7yhKeZ3wDSpcjDvvL2aasC25ng
- Admin API
- ADC
- Ingress Controller
创建消费者 jack:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "jack"
}'
为消费者创建 jwt-auth 凭据并配置 RSA 密钥:
curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-jack-jwt-auth",
"plugins": {
"jwt-auth": {
"key": "jack-key",
"algorithm": "RS256",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoTxe7ZPycrEP0SK4OBA2\n0OUQsDN9gSFSHVvx/t++nZNrFxzZnV6q6/TRsihNXUIgwaOu5icFlIcxPL9Mf9UJ\na5/XCQExp1TxpuSmjkhIFAJ/x5zXrC8SGTztP3SjkhYnQO9PKVXI6ljwgakVCfpl\numuTYqI+ev7e45NdK8gJoJxPp8bPMdf8/nHfLXZuqhO/btrDg1x+j7frDNrEw+6B\nCK2SsuypmYN+LwHfaH4Of7MQFk3LNIxyBz0mdbsKJBzp360rbWnQeauWtDymZxLT\nATRNBVyl3nCNsURRTkc7eyknLaDt2N5xTIoUGHTUFYSdE68QWmukYMVGcEHEEPkp\naQIDAQAB\n-----END PUBLIC KEY-----"
}
}
}'
❶ 将消费者密钥配置为 jack-key。
❷ 将 JWT 签名算法配置为 RS256。
❸ 配置 RSA 公钥。
你应该在起始行之后和结束行之前添加换行符,例如 -----BEGIN PUBLIC KEY-----\n......\n-----END PUBLIC KEY-----。
密钥内容可以直接拼接。
创建一个启用 jwt-auth 插件的路由:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "jwt-route",
"uri": "/headers",
"plugins": {
"jwt-auth": {}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
创建一个使用 RS256 算法的 jwt-auth 凭证消费者,并按如下方式创建一个启用了 jwt-auth 插件的路由:
consumers:
- username: jack
credentials:
- name: jwt-auth
type: jwt-auth
config:
key: jack-key
algorithm: RS256
public_key: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoTxe7ZPycrEP0SK4OBA2
0OUQsDN9gSFSHVvx/t++nZNrFxzZnV6q6/TRsihNXUIgwaOu5icFlIcxPL9Mf9UJ
a5/XCQExp1TxpuSmjkhIFAJ/x5zXrC8SGTztP3SjkhYnQO9PKVXI6ljwgakVCfpl
umuTYqI+ev7e45NdK8gJoJxPp8bPMdf8/nHfLXZuqhO/btrDg1x+j7frDNrEw+6B
CK2SsuypmYN+LwHfaH4Of7MQFk3LNIxyBz0mdbsKJBzp360rbWnQeauWtDymZxLT
ATRNBVyl3nCNsURRTkc7eyknLaDt2N5xTIoUGHTUFYSdE68QWmukYMVGcEHEEPkp
aQIDAQAB
-----END PUBLIC KEY-----
services:
- name: jwt-auth-service
routes:
- name: jwt-route
uris:
- /headers
plugins:
jwt-auth: {}
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
ADC 会将选定的顶层资源类型作为期望状态进行协调。标签选择器将此示例限制在其自身带标签的服务和消费者内;凭证仍嵌套在所属消费者下。请先预览限定范围的变更,并确认其中没有意外更新或删除:
adc diff -f adc.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=jwt-auth
同步已审查的配置:
adc sync -f adc.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=jwt-auth
- Gateway API
- APISIX CRD
创建一个使用 RS256 算法的 jwt-auth 凭证消费者,并按如下方式创建一个启用了 jwt-auth 插件的路由:
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: aic
name: jack
spec:
gatewayRef:
name: apisix
credentials:
- type: jwt-auth
name: primary-cred
config:
key: jack-key
algorithm: RS256
public_key: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoTxe7ZPycrEP0SK4OBA2
0OUQsDN9gSFSHVvx/t++nZNrFxzZnV6q6/TRsihNXUIgwaOu5icFlIcxPL9Mf9UJ
a5/XCQExp1TxpuSmjkhIFAJ/x5zXrC8SGTztP3SjkhYnQO9PKVXI6ljwgakVCfpl
umuTYqI+ev7e45NdK8gJoJxPp8bPMdf8/nHfLXZuqhO/btrDg1x+j7frDNrEw+6B
CK2SsuypmYN+LwHfaH4Of7MQFk3LNIxyBz0mdbsKJBzp360rbWnQeauWtDymZxLT
ATRNBVyl3nCNsURRTkc7eyknLaDt2N5xTIoUGHTUFYSdE68QWmukYMVGcEHEEPkp
aQIDAQAB
-----END PUBLIC KEY-----
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: jwt-auth-plugin-config
spec:
plugins:
- name: jwt-auth
config:
_meta:
disable: false
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: jwt-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /headers
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: jwt-auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
将配置应用到集群:
kubectl apply -f jwt-auth-ic.yaml
创建一个使用 RS256 算法的 jwt-auth 凭证消费者,并按如下方式创建一个启用了 jwt-auth 插件的路由:
apiVersion: apisix.apache.org/v2
kind: ApisixConsumer
metadata:
namespace: aic
name: jack
spec:
ingressClassName: apisix
authParameter:
jwtAuth:
value:
key: jack-key
algorithm: RS256
public_key: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyBhBBT5u2BtQs3+s2nnq
IXq9DRD8rWrmuk9lTI+rvzELaPZYzT7YxhBGuRJmbW+RnrIIB6dG6v9Kpn18qsvi
3u6UfsXKtoXckdk2tTCXSweNg1rzR9Szf/TxLSoi3KqA/0b/l9DqO9LYiWacEGgS
mqs0bCKtvxq+0TGQfuPHJiapvzgPTT1CYAp84CYDvyIo6d4NJOiPPSTEb1jxagSq
eLGZ3LVLZjSOC1kP4rbZP5U2VBMbkAtPtdFB1rOTCLykOQrH5eJxYxMkgiaDe9Da
ZilQ3vhGBTeqPL07NwOoiK0/iuBojMCdCKOdZfqgsBpEPP7qxqM3GNgPjAY0ah8x
awIDAQAB
-----END PUBLIC KEY-----
---
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: jwt-route
spec:
ingressClassName: apisix
http:
- name: jwt-route
match:
paths:
- /headers
upstreams:
- name: httpbin-external-domain
plugins:
- name: jwt-auth
enable: true
config:
_meta:
disable: false
将配置应用到集群:
kubectl apply -f jwt-auth-ic.yaml
要进行验证,请向路由发送在 Authorization 请求头中携带 JWT 的请求:
curl -i "http://127.0.0.1:9080/headers" -H "Authorization: ${jwt_token}"
你应该收到 HTTP/1.1 200 OK 响应。
将消费者自定义 ID 添加到请求头
以下示例演示了如何将消费者自定义 ID 添加到已认证请求的 Consumer-Custom-Id 请求头中,以便按需实现额外逻辑。
- Admin API
- ADC
- Ingress Controller
创建一个带有自定义 ID 标签的消费者 jack:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "jack",
"labels": {
"custom_id": "495aec6a"
}
}'
为消费者创建 jwt-auth 凭据:
curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-jack-jwt-auth",
"plugins": {
"jwt-auth": {
"key": "jack-key",
"secret": "jack-hs256-secret-that-is-very-long"
}
}
}'
创建一个启用 jwt-auth 的路由:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "jwt-auth-route",
"uri": "/anything",
"plugins": {
"jwt-auth": {}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
创建一个配置了 jwt-auth 凭证的消费者,以及一个按如下方式启用了 jwt-auth 插件的路由:
consumers:
- username: jack
labels:
custom_id: "495aec6a"
credentials:
- name: jwt-auth
type: jwt-auth
config:
key: jack-key
secret: jack-hs256-secret-that-is-very-long
services:
- name: jwt-auth-service
routes:
- name: jwt-auth-route
uris:
- /anything
plugins:
jwt-auth: {}
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
ADC 会将选定的顶层资源类型作为期望状态进行协调。标签选择器将此示例限制在其自身带标签的服务和消费者内;凭证仍嵌套在所属消费者下。请先预览限定范围的变更,并确认其中没有意外更新或删除:
adc diff -f adc.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=jwt-auth
同步已审查的配置:
adc sync -f adc.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=jwt-auth
创建一个配置了 jwt-auth 凭证的消费者,以及一个启用了 jwt-auth 插件的路由:
- Gateway API
- APISIX CRD
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: aic
name: jack
labels:
custom_id: "495aec6a"
spec:
gatewayRef:
name: apisix
credentials:
- type: jwt-auth
name: primary-cred
config:
key: jack-key
secret: jack-hs256-secret-that-is-very-long
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: jwt-auth-plugin-config
spec:
plugins:
- name: jwt-auth
config:
_meta:
disable: false
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: jwt-auth-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: jwt-auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
将配置应用到集群:
kubectl apply -f jwt-auth-ic.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixConsumer
metadata:
namespace: aic
name: jack
labels:
custom_id: "495aec6a"
spec:
ingressClassName: apisix
authParameter:
jwtAuth:
value:
key: jack-key
secret: jack-hs256-secret-that-is-very-long
---
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: jwt-auth-route
spec:
ingressClassName: apisix
http:
- name: jwt-auth-route
match:
paths:
- /anything
upstreams:
- name: httpbin-external-domain
plugins:
- name: jwt-auth
enable: true
config:
_meta:
disable: false
将配置应用到集群:
kubectl apply -f jwt-auth-ic.yaml
要为 jack 签发 JWT,你可以使用 JWT.io 的 JWT 编码器 或其他工具。如果你使用的是 JWT.io 的 JWT 编码器,请执行以下操作:
- 在算法栏填写
HS256。 - 在 Valid secret 部分将密钥更新为
jack-hs256-secret-that-is-very-long。 - 使用消费者密钥
jack-key更新 payload;并添加 UNIX 时间戳格式的exp或nbf。
当 claims_to_verify 为非空列表时,列表中的每个 Claim 都必须存在并通过验证。未设置或设置为空列表时,如果 exp 和 nbf 存在,则会对其进行验证,但不会强制要求这两个 Claim 存在。
你的 payload 应该类似于以下内容:
{
"key": "jack-key",
"nbf": 1729132271
}
复制生成的 JWT 并保存到变量中:
export jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU
要进行验证,请向路由发送在 Authorization 请求头中携带 JWT 的请求:
curl -i "http://127.0.0.1:9080/anything" -H "Authorization: ${jwt_token}"
你应该看到类似于以下的 HTTP/1.1 200 OK 响应:
{
"headers": {
"Accept": "*/*",
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU",
"Host": "127.0.0.1",
"User-Agent": "curl/8.6.0",
"X-Amzn-Trace-Id": "Root=1-6873b19d-329331db76e5e7194c942b47",
"X-Consumer-Custom-Id": "495aec6a",
"X-Consumer-Username": "aic_jack",
"X-Forwarded-Host": "127.0.0.1"
},
"url": "http://127.0.0.1/anything"
}
如果你想将更多消费者自定义请求头添加到已认证请求中,请参阅 attach-consumer-label 插件。