使用APISIX解析jwt并获取payload信息
APISIX支持获取jwt的信息,并且将这个信息进行解码并转发给后端服务。
1. 启动服务
首先我们根据官方脚本来启动APISIX服务
~ curl -sL "https://run.api7.ai/apisix/quickstart" | sh
Destroying existing apisix-quickstart container, if any.
Installing APISIX with the quickstart options.
Creating bridge network apisix-quickstart-net.
77e35df073894075ad77facd9d1c7d2a35b280213732c1b631052caede079bab
✔ network apisix-quickstart-net created
Starting the container etcd-quickstart.
d123605c8b7658b130be97e5f44e7a160aa85858db008032ecf594266225e342
✔ etcd is listening on etcd-quickstart:2379
Starting the container apisix-quickstart.
38434806c63b3a72f53fb6ad849cb4c11781eebaff79c8db04510226593fcf46
⚠ WARNING: The Admin API key is currently disabled. You should turn on admin_key_required and set a strong Admin API key in production for security.
✔ APISIX is ready!
2. 配置插件
启动了APISIX之后,我们首先创建一个插件配置。在这个插件中我们定义了一个Lua方法,这个方法的目的是从请求的header中获取authorization信息,并进行解码,之后将解码的信息放到HTTP header中传给后端
curl --location --request PUT 'http://127.0.0.1:9180/apisix/admin/plugin_configs/1001' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: 127.0.0.1:9180' \
--header 'Connection: keep-alive' \
--data-raw '{
"plugins": {
"serverless-pre-function": {
"phase": "access",
"functions": [
"return function(_, ctx) local core = require(\"apisix.core\") local jwt = require(\"resty.jwt\") local auth_header = ctx.var.http_authorization if not auth_header then return end local token = auth_header:match(\"Bearer%s+(.+)\") if not token then return end local obj = jwt:load_jwt(token) if obj and obj.valid and obj.payload then if obj.payload.user_id then core.request.set_header(\"X-User-Id\", obj.payload.user_id) end if obj.payload.role then core.request.set_header(\"X-User-Role\", obj.payload.role) end end end"
]
}
}
}'
如上的fucntions属性中添加了一个Lua方法,格式化之后的Lua代码如下
1 | return function(_, ctx) |
这段代码实现了如下几个功能:
- 从 Authorization: Bearer
中提取 JWT - 使用 resty.jwt 解码
- 如果合法,提取 user_id 和 role
- 注入到 header(X-User-Id, X-User-Role)中供后端读取
3. 配置consumer
创建了这个插件之后,我们再新建一个consumer。在APISIX中,consumer代表了一类客户端,比如APP。我们可以针对这类客户端添加一些配置,多种不同类型的客户端(比如APP、网页、开放平台,等等)可以分别设置成不同的consumer以方便管理
curl --location --request PUT 'http://127.0.0.1:9180/apisix/admin/consumers/app' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: 127.0.0.1:9180' \
--header 'Connection: keep-alive' \
--data-raw '{
"username": "app",
"plugins": {
"jwt-auth": {
"key": "app-key",
"secret": "a-string-secret-at-least-256-bits-long",
"algorithm": "HS256"
}
}
}'
如上添加了一个名为app的consumer,它的key是app-key
,加密方式是HS256
,密钥是a-string-secret-at-least-256-bits-long
。有了解析插件和consumer之后,我们就可以创建路由了。
4. 配置路由
如下请求会创建一个ID为1
的路由,使用了ID为1001
插件,并且添加了jwt-auth
的配置,路由的后端是https://httpbin.org,这个网站会把我们请求的信息返回给我们。
curl --location --request PUT 'http://127.0.0.1:9180/apisix/admin/routes/1' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: 127.0.0.1:9180' \
--header 'Connection: keep-alive' \
--data-raw '{
"uri": "/headers",
"plugin_config_id": 1001,
"plugins": {
"jwt-auth": {}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
5. 发起请求
在创建好了plugin_config、consumer和route之后,我们就可以测试请求了。首先我们构建如下payload
1 | { |
这个payload包含了user_id
和role
两个业务属性,exp代表这个jwt的过期时间戳,key是APISIX用于识别匹配哪个consumer的,这里我们选择匹配app-key
这个consumer。之后我们将该payload和密钥a-string-secret-at-least-256-bits-long
一起在https://jwt.io/进行编码,得到编码jwt信息如下
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJhcHAta2V5IiwidXNlcl9pZCI6MTAwMDAxLCJyb2xlIjoiYWRtaW4iLCJleHAiOjE5MDAwMDAwMDB9.qG7PNPz2XlatmjrhNW_xf6SmI8T9JSIx2lJVJcAox0I
之后我们执行HTTP请求,将这个jwt放到Authorization header中
curl --location --request GET 'http://127.0.0.1:9080/headers' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJhcHAta2V5IiwidXNlcl9pZCI6MTAwMDAxLCJyb2xlIjoiYWRtaW4iLCJleHAiOjE5MDAwMDAwMDB9.qG7PNPz2XlatmjrhNW_xf6SmI8T9JSIx2lJVJcAox0I' \
--header 'Accept: */*' \
--header 'Host: httpbin.org:80' \
--header 'Connection: keep-alive'
请求得到的响应如下,可以看到user_id和role属性已经成功的传给后端服务了
1 | { |