实施 JWT 认证
JWT 认证是一种使用 JSON Web Token (JWT) 对 API 客户端进行身份验证的安全且无状态的方法。它涉及在成功身份验证后向客户端颁发签名令牌,客户端在后续请求中包含该令牌。JWT 封装了用户身份和角色等声明,使 API 无需查询后端数据库即可验证请求。此方法非常适合需要轻量级和快速身份验证的可扩展、分布式应用程序。但是,由于 JWT 在过期之前一直有效,因此必须小心使用较短的过期时间或实施令牌撤销以增加安全性。
在本指南中,你将实现一个场景,其中有两个消费者使用 JWT 认证 向 APISIX 进行身份验证,每个消费者具有不同的限流限速配额。一旦实施,消费者应该能够访问上游服务并将消费者 ID 转发到上游服务,从而为额外的业务逻辑提供选项。
创建消费者
消费者是指使用 API 的应用程序或开发者。在使用 APISIX 内置身份验证方法时,你应该始终创建消费者。
- Admin API
- Ingress Controller
创建一个具有可选自定义 ID 和 30 秒窗口内一个请求的限流限速配额的消费者 johndoe:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "johndoe",
"labels": {
"custom_id": "john-doe-junior"
},
"plugins": {
"limit-count": {
"count": 1,
"time_window": 30,
"rejected_code": 429
}
}
}'
如果你希望实施额外的业务逻辑,自定义 ID 将转发到上游服务。
创建另一个具有可选自定义 ID 和 30 秒窗口内两个请求的限流限速配额的消费者 janedoe:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "janedoe",
"labels": {
"custom_id": "jane-doe-senior"
},
"plugins": {
"limit-count": {
"count": 2,
"time_window": 30,
"rejected_code": 429
}
}
}'
Ingress Controller 目前不支持配置消费者标签。
- Gateway API
- APISIX CRD
为消费者 johndoe 创建一个 Kubernetes 清单文件,其限流限速配额为 30 秒窗口内一个请求:
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: ingress-apisix
name: johndoe
spec:
gatewayRef:
name: apisix
plugins:
- name: limit-count
config:
count: 1
time_window: 30
rejected_code: 429
为消费者 janedoe 创建另一个 Kubernetes 清单文件,其限流限速配额为 30 秒窗口内两个请求:
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: ingress-apisix
name: janedoe
spec:
gatewayRef:
name: apisix
plugins:
- name: limit-count
config:
count: 2
time_window: 30
rejected_code: 429
将配置应用到你的集群:
kubectl apply -f consumer-rate-limit-john.yaml -f consumer-rate-limit-jane.yaml
ApisixConsumer CRD 目前不支持针对本指南中的场景在消费者上配置插件。要配置具有 jwt-auth 但不带限流限速的消费者,请参阅 配置消费者和凭证。
创建消费者凭证
凭证用于配置与消费者关联的身份验证凭证。
- Admin API
- Ingress Controller
为 johndoe 创建 jwt-auth 凭证:
curl "http://127.0.0.1:9180/apisix/admin/consumers/johndoe/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-john-jwt-auth",
"plugins": {
"jwt-auth": {
"key": "john-key",
"secret": "john-hs256-secret-that-is-very-long"
}
}
}'
为 janedoe 创建 jwt-auth 凭证:
curl "http://127.0.0.1:9180/apisix/admin/consumers/janedoe/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-jane-jwt-auth",
"plugins": {
"jwt-auth": {
"key": "jane-key",
"secret": "jane-hs256-secret-that-is-very-long"
}
}
}'
- Gateway API
- APISIX CRD
使用凭证更新 Kubernetes 清单文件:
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: ingress-apisix
name: johndoe
spec:
gatewayRef:
name: apisix
credentials:
- type: jwt-auth
name: cred-john-jwt-auth
config:
key: john-key
secret: john-hs256-secret-that-is-very-long
plugins:
- name: limit-count
config:
count: 1
time_window: 30
rejected_code: 429
apiVersion: apisix.apache.org/v1alpha1
kind: Consumer
metadata:
namespace: ingress-apisix
name: janedoe
spec:
gatewayRef:
name: apisix
credentials:
- type: jwt-auth
name: cred-jane-jwt-auth
config:
key: jane-key
secret: jane-hs256-secret-that-is-very-long
plugins:
- name: limit-count
config:
count: 2
time_window: 30
rejected_code: 429
将配置应用到你的集群:
kubectl apply -f consumer-rate-limit-john.yaml -f consumer-rate-limit-jane.yaml
The ApisixConsumer CRD currently does not support configuring plugins on consumers for the scenario in this guide. To configure a consumer with jwt-auth without rate limiting, see Configure Consumer and Credentials.
创建路由
创建一个路由并启用 jwt-auth:
- Admin API
- Ingress Controller
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
}
}
}'
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: ingress-apisix
name: auth-plugin-config
spec:
plugins:
- name: jwt-auth
config:
_meta:
disable: false
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: ingress-apisix
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: auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: ingress-apisix
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
将配置应用到你的集群:
kubectl apply -f httpbin-route.yaml
签发 JWT
要为 johndoe 签发 JWT,你可以使用 JWT.io 的 JWT 编码器 或其他工具。如果你使用 JWT.io 的 JWT 编码器,请执行以下操作:
- 填写
HS256作为算法。 - 在 Valid secret 部分中将密钥更新为
john-hs256-secret-that-is-very-long。 - 使用消费者密钥
john-key更新有效负载;并将exp或nbf添加为 UNIX 时间戳。
如果你使用的是 API7 企业版,exp 或 nbf 的要求不是强制性的。你可以选择包含这些声明,并使用 claims_to_verify 参数来配置要验证的声明。
你的有效负载应类似于以下内容:
{
"key": "john-key",
"nbf": 1729132271
}
复制生成的 JWT 并保存到变量:
export john_jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqb2huLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.TgoM2efMEaSX7KrIWujGLGK5dpPGS2nbaiinmVVjKKw
对 janedoe 重复该步骤并将生成的 JWT 保存到 jane_jwt_token。
验证
使用 john 的密钥向路由发送请求:
curl -i "http://127.0.0.1:9080/anything" -H "Authorization: ${john_jwt_token}"
你应该看到类似以下的 HTTP/1.1 200 OK 响应:
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqb2huLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.TgoM2efMEaSX7KrIWujGLGK5dpPGS2nbaiinmVVjKKw",
"Host": "127.0.0.1",
"User-Agent": "curl/8.6.0",
"X-Amzn-Trace-Id": "Root=1-6873b51b-5502cfcd72380323266c22e8",
"X-Consumer-Custom-Id": "john-doe-junior",
"X-Consumer-Username": "johndoe",
"X-Credential-Identifier": "cred-john-jwt-auth",
"X-Forwarded-Host": "127.0.0.1"
},
...
}
使用 john 的密钥向路由生成三个请求:
resp=$(seq 3 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H "Authorization: ${john_jwt_token}" -o /dev/null -s -w "%{http_code}\n") && \
count_200=$(echo "$resp" | grep "200" | wc -l) && \
count_429=$(echo "$resp" | grep "429" | wc -l) && \
echo "200": $count_200, "429": $count_429
你应该看到以下响应,显示在 3 个请求中,1 个请求成功,而其他请求被拒绝:
200: 1, 429: 2
使用 jane 的密钥向路由生成三个请求:
resp=$(seq 3 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H "Authorization: ${jane_jwt_token}" -o /dev/null -s -w "%{http_code}\n") && \
count_200=$(echo "$resp" | grep "200" | wc -l) && \
count_429=$(echo "$resp" | grep "429" | wc -l) && \
echo "200": $count_200, "429": $count_429
你应该看到以下响应,显示在 3 个请求中,2 个请求成功,而另一个被拒绝:
200: 2, 429: 1
最后,发送一个带有无效密钥的请求:
curl -i "http://127.0.0.1:9080/anything" -H 'Authorization: somewrongkey'
你应该看到 HTTP/1.1 401 Unauthorized 响应,并带有以下消息:
{"message":"JWT token invalid"}
下一步
你现在已经学会了如何实施基本认证。APISIX 支持其他内置身份验证方法,例如 密钥认证、基本认证 和 HMAC 认证。