title: opa keywords:
import Tabs from ‘@theme/Tabs’; import TabItem from ‘@theme/TabItem’;
opa 插件支持与 Open Policy Agent (OPA) 集成,OPA 是一个统一的策略引擎和框架,可用于定义和执行授权策略。授权逻辑以 Rego 语言编写并存储在 OPA 中。
配置后,OPA 引擎将根据定义的策略评估客户端对受保护路由的请求,以决定是否允许其访问上游资源。
| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 |
|---|---|---|---|---|---|
| host | string | 是 | OPA 服务器地址。 | ||
| policy | string | 是 | 要评估的策略路径。该值会被直接拼接到 OPA 的 /v1/data/<policy>。data.result 必须是一个包含 allow 字段的对象,并可按需包含 reason、headers、status_code 等字段;因此,policy 应指向返回该对象结构的路径,而不是仅返回 true 或 false 的布尔规则。 | ||
| ssl_verify | boolean | 否 | true | 若为 true,则验证 OPA 服务器的 SSL 证书。 | |
| timeout | integer | 否 | 3000 | [1, 60000] | HTTP 调用超时时间(毫秒)。 |
| keepalive | boolean | 否 | true | 若为 true,则为多个请求保持连接活跃。 | |
| keepalive_timeout | integer | 否 | 60000 | >= 1000 | 连接空闲后关闭的等待时间(毫秒)。 |
| keepalive_pool | integer | 否 | 5 | >= 1 | 空闲连接数。 |
| with_route | boolean | 否 | 若为 true,发送当前路由的信息。 | ||
| with_service | boolean | 否 | 若为 true,发送当前 Service 的信息。 | ||
| with_consumer | boolean | 否 | 若为 true,发送当前消费者的信息。注意,Consumer 信息可能包含 API key 等敏感信息,仅在确认安全的情况下将此选项设为 true。 | ||
| send_headers_upstream | array[string] | 否 | >= 1 | 请求被允许时,需要从 OPA 响应转发到上游服务的请求头名称列表。 |
下述示例展示了 APISIX 向 OPA 服务发送的数据格式:
{ "type": "http", "request": { "scheme": "http", "path": "\/get", "headers": { "user-agent": "curl\/7.68.0", "accept": "*\/*", "host": "127.0.0.1:9080" }, "query": {}, "port": 9080, "method": "GET", "host": "127.0.0.1" }, "var": { "timestamp": 1701234567, "server_addr": "127.0.0.1", "server_port": "9080", "remote_port": "port", "remote_addr": "ip address" }, "route": {}, "service": {}, "consumer": {} }
各字段说明如下:
type 表示请求类型(http 或 stream)。request 在 type 为 http 时使用,包含基本的请求信息(URL、请求头等)。var 包含请求连接的基本信息(IP、端口、请求时间戳等)。route、service 和 consumer 包含与 APISIX 中存储的相同数据,仅在这些对象上配置了 opa 插件时才会发送。下述示例展示了 OPA 服务对 APISIX 的响应数据格式:
{ "result": { "allow": true, "reason": "test", "headers": { "an": "header" }, "status_code": 401 } }
各字段说明如下:
allow 是必填字段,表示请求是否允许通过 APISIX 转发。reason、headers 和 status_code 是可选字段,仅在配置了自定义响应时返回。:::note
你可以这样从 conf/config.yaml 中获取 admin_key 并存入环境变量:
admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')
:::
在开始之前,你需要一个运行中的 OPA 服务器。可以通过 Docker 启动或部署到 Kubernetes:
<Tabs groupId=“opa-setup” defaultValue=“docker” values={[ {label: ‘Docker’, value: ‘docker’}, {label: ‘Kubernetes’, value: ‘kubernetes’} ]}>
docker run -d --name opa-server -p 8181:8181 openpolicyagent/opa:1.6.0 run --server --addr :8181 --log-level debug
验证 OPA 服务器安装正常且端口已正确暴露:
curl http://127.0.0.1:8181 | grep Version
你应该看到类似如下的响应:
Version: 1.6.0
在集群中创建 OPA 的 Deployment 和 Service:
apiVersion: apps/v1 kind: Deployment metadata: namespace: aic name: opa spec: replicas: 1 selector: matchLabels: app: opa template: metadata: labels: app: opa spec: containers: - name: opa image: openpolicyagent/opa:1.6.0 args: - run - --server - --addr=:8181 - --log-level=debug ports: - containerPort: 8181 --- apiVersion: v1 kind: Service metadata: namespace: aic name: opa spec: selector: app: opa ports: - port: 8181 targetPort: 8181
将配置应用到集群:
kubectl apply -f opa-server.yaml
等待 OPA Pod 就绪。就绪后,OPA 服务器将在集群内通过 http://opa.aic.svc.cluster.local:8181 访问。如需从集群外部推送策略,请设置端口转发:
kubectl port-forward -n aic svc/opa 8181:8181 &
以下示例在 OPA 中实现一个仅允许 GET 请求的基本授权策略。
创建一个仅允许 HTTP GET 请求的 OPA 策略:
curl "http://127.0.0.1:8181/v1/policies/getonly" -X PUT \ -H "Content-Type: text/plain" \ -d ' package getonly default allow = false allow if { input.request.method == "GET" }'
创建带有 opa 插件的路由:
<Tabs groupId=“api” defaultValue=“admin-api” values={[ {label: ‘Admin API’, value: ‘admin-api’}, {label: ‘ADC’, value: ‘adc’}, {label: ‘Ingress Controller’, value: ‘aic’} ]}>
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${admin_key}" \ -d '{ "id": "opa-route", "uri": "/anything", "plugins": { "opa": { "host": "http://127.0.0.1:8181", "policy": "getonly" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }'
将 host 替换为你的 OPA 服务器地址,policy 设置为 getonly。
services: - name: opa-service routes: - name: opa-route uris: - /anything plugins: opa: host: "http://127.0.0.1:8181" policy: getonly upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1
将 host 替换为你的 OPA 服务器地址,policy 设置为 getonly。
将配置同步到网关:
adc sync -f adc.yaml
<Tabs groupId=“k8s-api” defaultValue=“gateway-api” values={[ {label: ‘Gateway API’, value: ‘gateway-api’}, {label: ‘APISIX Ingress Controller’, value: ‘apisix-ingress-controller’} ]}>
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: opa-plugin-config spec: plugins: - name: opa config: host: "http://opa.aic.svc.cluster.local:8181" policy: getonly --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: aic name: opa-route spec: parentRefs: - name: apisix rules: - matches: - path: type: PathPrefix value: /anything filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: opa-plugin-config backendRefs: - name: httpbin-external-domain port: 80
将配置应用到集群:
kubectl apply -f opa-ic.yaml
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: opa-route spec: ingressClassName: apisix http: - name: opa-route match: paths: - /anything upstreams: - name: httpbin-external-domain plugins: - name: opa enable: true config: host: "http://opa.aic.svc.cluster.local:8181" policy: getonly
将配置应用到集群:
kubectl apply -f opa-ic.yaml
向路由发送 GET 请求以验证策略:
curl -i "http://127.0.0.1:9080/anything"
你应该收到 HTTP/1.1 200 OK 响应。
使用 PUT 方法向路由发送请求:
curl -i "http://127.0.0.1:9080/anything" -X PUT
你应该收到 HTTP/1.1 403 Forbidden 响应。
以下示例帮助你了解 APISIX 推送给 OPA 的数据格式,以便编写授权逻辑。该示例基于上一个示例中的策略和路由。
更新之前创建的路由上的插件,使其包含路由信息:
<Tabs groupId=“api” defaultValue=“admin-api” values={[ {label: ‘Admin API’, value: ‘admin-api’}, {label: ‘ADC’, value: ‘adc’}, {label: ‘Ingress Controller’, value: ‘aic’} ]}>
curl "http://127.0.0.1:9180/apisix/admin/routes/opa-route" -X PATCH \ -H "X-API-KEY: ${admin_key}" \ -d '{ "plugins": { "opa": { "with_route": true } } }'
更新 adc.yaml,添加 with_route: true:
services: - name: opa-service routes: - name: opa-route uris: - /anything plugins: opa: host: "http://127.0.0.1:8181" policy: getonly with_route: true upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1
将配置同步到网关:
adc sync -f adc.yaml
<Tabs groupId=“k8s-api” defaultValue=“gateway-api” values={[ {label: ‘Gateway API’, value: ‘gateway-api’}, {label: ‘APISIX Ingress Controller’, value: ‘apisix-ingress-controller’} ]}>
更新 opa-ic.yaml,添加 with_route: true:
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: opa-plugin-config spec: plugins: - name: opa config: host: "http://opa.aic.svc.cluster.local:8181" policy: getonly with_route: true --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: aic name: opa-route spec: parentRefs: - name: apisix rules: - matches: - path: type: PathPrefix value: /anything filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: opa-plugin-config backendRefs: - name: httpbin-external-domain port: 80
将更新后的配置应用到集群:
kubectl apply -f opa-ic.yaml
更新 opa-ic.yaml,添加 with_route: true:
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: opa-route spec: ingressClassName: apisix http: - name: opa-route match: paths: - /anything upstreams: - name: httpbin-external-domain plugins: - name: opa enable: true config: host: "http://opa.aic.svc.cluster.local:8181" policy: getonly with_route: true
将更新后的配置应用到集群:
kubectl apply -f opa-ic.yaml
向路由发送请求:
curl -i "http://127.0.0.1:9080/anything"
在 OPA 服务器日志(启用 --log-level debug)中,req_body 将在请求和变量字段之外还包含路由信息。
以下示例演示如何在请求未授权时返回自定义响应码和消息。
创建一个仅允许 HTTP GET 请求、并在未授权时返回 302 及自定义消息的 OPA 策略:
curl "http://127.0.0.1:8181/v1/policies/customresp" -X PUT \ -H "Content-Type: text/plain" \ -d ' package customresp default allow = false allow if { input.request.method == "GET" } reason := "The resource has temporarily moved. Please follow the new URL." if { not allow } headers := { "Location": "http://example.com/auth" } if { not allow } status_code := 302 if { not allow } '
创建带有 opa 插件的路由:
<Tabs groupId=“api” defaultValue=“admin-api” values={[ {label: ‘Admin API’, value: ‘admin-api’}, {label: ‘ADC’, value: ‘adc’}, {label: ‘Ingress Controller’, value: ‘aic’} ]}>
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${admin_key}" \ -d '{ "id": "opa-route", "uri": "/anything", "plugins": { "opa": { "host": "http://127.0.0.1:8181", "policy": "customresp" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }'
services: - name: opa-service routes: - name: opa-route uris: - /anything plugins: opa: host: "http://127.0.0.1:8181" policy: customresp upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1
将配置同步到网关:
adc sync -f adc.yaml
<Tabs groupId=“k8s-api” defaultValue=“gateway-api” values={[ {label: ‘Gateway API’, value: ‘gateway-api’}, {label: ‘APISIX Ingress Controller’, value: ‘apisix-ingress-controller’} ]}>
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: opa-customresp-plugin-config spec: plugins: - name: opa config: host: "http://opa.aic.svc.cluster.local:8181" policy: customresp --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: aic name: opa-route spec: parentRefs: - name: apisix rules: - matches: - path: type: PathPrefix value: /anything filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: opa-customresp-plugin-config backendRefs: - name: httpbin-external-domain port: 80
将配置应用到集群:
kubectl apply -f opa-ic.yaml
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: opa-route spec: ingressClassName: apisix http: - name: opa-route match: paths: - /anything upstreams: - name: httpbin-external-domain plugins: - name: opa enable: true config: host: "http://opa.aic.svc.cluster.local:8181" policy: customresp
将配置应用到集群:
kubectl apply -f opa-ic.yaml
向路由发送 GET 请求:
curl -i "http://127.0.0.1:9080/anything"
你应该收到 HTTP/1.1 200 OK 响应。
向路由发送 POST 请求:
curl -i "http://127.0.0.1:9080/anything" -X POST
你应该收到 HTTP/1.1 302 Moved Temporarily 响应:
HTTP/1.1 302 Moved Temporarily ... Location: http://example.com/auth The resource has temporarily moved. Please follow the new URL.
以下示例演示如何结合 jwt-auth 和 opa 插件实现认证和 RBAC,其中:
user 角色仅能读取上游资源。admin 角色可以读取和写入上游资源。为示例消费者 john(user 角色)和 jane(admin 角色)创建 OPA RBAC 策略:
curl "http://127.0.0.1:8181/v1/policies/rbac" -X PUT \ -H "Content-Type: text/plain" \ -d ' package rbac # Assign roles to users user_roles := { "john": ["user"], "jane": ["admin"] } # Map permissions to HTTP methods permission_methods := { "read": "GET", "write": "POST" } # Assign role permissions role_permissions := { "user": ["read"], "admin": ["read", "write"] } # Get JWT authorization token bearer_token := t if { t := input.request.headers.authorization } # Decode the token to get role and permission token := {"payload": payload} if { [_, payload, _] := io.jwt.decode(bearer_token) } # Normalize permission to a list normalized_permissions := ps if { ps := token.payload.permission not is_string(ps) } normalized_permissions := [ps] if { ps := token.payload.permission is_string(ps) } # Implement RBAC logic default allow = false allow if { # Look up the list of roles for the user roles := user_roles[input.consumer.username] # For each role in that list r := roles[_] # Look up the permissions list for the role permissions := role_permissions[r] # For each permission p := permissions[_] # Check if the permission matches the request method permission_methods[p] == input.request.method # Check if the normalized permissions include the permission p in normalized_permissions } '
在 APISIX 中创建消费者 john 和 jane,并配置 jwt-auth 凭证:
<Tabs groupId=“api” defaultValue=“admin-api” values={[ {label: ‘Admin API’, value: ‘admin-api’}, {label: ‘ADC’, value: ‘adc’}, {label: ‘Ingress Controller’, value: ‘aic’} ]}>
curl "http://127.0.0.1:9180/apisix/admin/consumers" \ -X PUT \ -H "X-API-KEY: ${admin_key}" \ -H "Content-Type: application/json" \ -d '{ "username": "john" }'
curl "http://127.0.0.1:9180/apisix/admin/consumers" \ -X PUT \ -H "X-API-KEY: ${admin_key}" \ -H "Content-Type: application/json" \ -d '{ "username": "jane" }'
为消费者配置 jwt-auth 凭证,使用默认算法 HS256:
curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \ -H "X-API-KEY: ${admin_key}" \ -d '{ "id": "cred-john-jwt-auth", "plugins": { "jwt-auth": { "key": "john-key", "secret": "john-hs256-secret-that-is-very-long" } } }'
curl "http://127.0.0.1:9180/apisix/admin/consumers/jane/credentials" -X PUT \ -H "X-API-KEY: ${admin_key}" \ -d '{ "id": "cred-jane-jwt-auth", "plugins": { "jwt-auth": { "key": "jane-key", "secret": "jane-hs256-secret-that-is-very-long" } } }'
consumers: - username: john credentials: - name: cred-john-jwt-auth type: jwt-auth config: key: john-key secret: john-hs256-secret-that-is-very-long - username: jane credentials: - name: cred-jane-jwt-auth type: jwt-auth config: key: jane-key secret: jane-hs256-secret-that-is-very-long
将配置同步到网关:
adc sync -f adc.yaml
<Tabs groupId=“k8s-api” defaultValue=“gateway-api” values={[ {label: ‘Gateway API’, value: ‘gateway-api’}, {label: ‘APISIX Ingress Controller’, value: ‘apisix-ingress-controller’} ]}>
apiVersion: apisix.apache.org/v1alpha1 kind: Consumer metadata: namespace: aic name: john 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 --- apiVersion: apisix.apache.org/v1alpha1 kind: Consumer metadata: namespace: aic name: jane 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
将配置应用到集群:
kubectl apply -f opa-consumers-ic.yaml
使用 Ingress Controller 时,APISIX 会在消费者名称前加上 Kubernetes 命名空间前缀。例如,aic 命名空间中名为 john 的消费者会变为 aic_john。请相应更新 OPA RBAC 策略中的用户名。
ApisixConsumer CRD 存在已知问题,private_key 在配置时被错误地设为必填项。该问题将在未来版本中修复。目前无法通过 APISIX CRD 完成此示例。
创建路由并配置 jwt-auth 和 opa 插件:
<Tabs groupId=“api” defaultValue=“admin-api” values={[ {label: ‘Admin API’, value: ‘admin-api’}, {label: ‘ADC’, value: ‘adc’}, {label: ‘Ingress Controller’, value: ‘aic’} ]}>
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${admin_key}" \ -d '{ "id": "opa-route", "methods": ["GET", "POST"], "uris": ["/get","/post"], "plugins": { "jwt-auth": {}, "opa": { "host": "http://127.0.0.1:8181", "policy": "rbac", "with_consumer": true } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }'
更新 adc.yaml,添加带有 jwt-auth 和 opa 插件的路由:
consumers: - username: john credentials: - name: cred-john-jwt-auth type: jwt-auth config: key: john-key secret: john-hs256-secret-that-is-very-long - username: jane credentials: - name: cred-jane-jwt-auth type: jwt-auth config: key: jane-key secret: jane-hs256-secret-that-is-very-long services: - name: opa-service routes: - name: opa-route uris: - /get - /post methods: - GET - POST plugins: jwt-auth: {} opa: host: "http://127.0.0.1:8181" policy: rbac with_consumer: true upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1
将配置同步到网关:
adc sync -f adc.yaml
<Tabs groupId=“k8s-api” defaultValue=“gateway-api” values={[ {label: ‘Gateway API’, value: ‘gateway-api’}, {label: ‘APISIX Ingress Controller’, value: ‘apisix-ingress-controller’} ]}>
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: opa-rbac-plugin-config spec: plugins: - name: jwt-auth config: _meta: disable: false - name: opa config: host: "http://opa.aic.svc.cluster.local:8181" policy: rbac with_consumer: true --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: aic name: opa-rbac-route spec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /get method: GET filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: opa-rbac-plugin-config backendRefs: - name: httpbin-external-domain port: 80 - matches: - path: type: Exact value: /post method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: opa-rbac-plugin-config backendRefs: - name: httpbin-external-domain port: 80
将配置应用到集群:
kubectl apply -f opa-route-ic.yaml
ApisixConsumer CRD 存在已知问题,private_key 在配置时被错误地设为必填项。该问题将在未来版本中修复。目前无法通过 APISIX CRD 完成此示例。
john 身份验证要为 john 生成 JWT,可使用 JWT.io 的 JWT 编码器 或其他工具。若使用 JWT.io 的 JWT 编码器,请执行以下操作:
HS256。john-hs256-secret-that-is-very-long。user、权限 read、Consumer key john-key,以及 UNIX 时间戳格式的 exp 或 nbf。你的 payload 应类似如下:
{ "role": "user", "permission": "read", "key": "john-key", "nbf": 1729132271 }
复制生成的 JWT 并存入变量:
export john_jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsInBlcm1pc3Npb24iOiJyZWFkIiwia2V5Ijoiam9obi1rZXkiLCJuYmYiOjE3MjkxMzIyNzF9.rAHMTQfnnGFnKYc3am_lpE9pZ9E8EaOT_NBQ5Ss8pk4
使用 john 的 JWT 向路由发送 GET 请求:
curl -i "http://127.0.0.1:9080/get" -H "Authorization: Bearer ${john_jwt_token}"
你应该收到 HTTP/1.1 200 OK 响应。
使用相同 JWT 向路由发送 POST 请求:
curl -i "http://127.0.0.1:9080/post" -X POST -H "Authorization: Bearer ${john_jwt_token}"
你应该收到 HTTP/1.1 403 Forbidden 响应。
jane 身份验证同样地,为 jane 生成 JWT,可使用 JWT.io 的 JWT 编码器 或其他工具。若使用 JWT.io 的 JWT 编码器,请执行以下操作:
HS256。jane-hs256-secret-that-is-very-long。admin、权限 ["read","write"]、Consumer key jane-key,以及 UNIX 时间戳格式的 exp 或 nbf。你的 payload 应类似如下:
{ "role": "admin", "permission": ["read","write"], "key": "jane-key", "nbf": 1729132271 }
复制生成的 JWT 并存入变量:
export jane_jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJwZXJtaXNzaW9uIjpbInJlYWQiLCJ3cml0ZSJdLCJrZXkiOiJqYW5lLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.meZ-AaGHUPwN_GvVOE3IkKuAJ1wqlCguaXf3gm3Ww8s
使用 jane 的 JWT 向路由发送 GET 请求:
curl -i "http://127.0.0.1:9080/get" -H "Authorization: Bearer ${jane_jwt_token}"
你应该收到 HTTP/1.1 200 OK 响应。
使用相同 JWT 向路由发送 POST 请求:
curl -i "http://127.0.0.1:9080/post" -X POST -H "Authorization: Bearer ${jane_jwt_token}"
你也应该收到 HTTP/1.1 200 OK 响应。