APISIX支持获取jwt的信息,并且将这个信息进行解码并转发给后端服务。
1. 启动服务
首先我们根据官方脚本来启动APISIX服务
~ curl -sL "https://run.api7.ai/apisix/quickstart" | shDestroying existing apisix-quickstart container, if any.Installing APISIX with the quickstart options.Creating bridge network apisix-quickstart-net.77e35df073894075ad77facd9d1c7d2a35b280213732c1b631052caede079bab✔ network apisix-quickstart-net createdStarting the container etcd-quickstart.d123605c8b7658b130be97e5f44e7a160aa85858db008032ecf594266225e342✔ etcd is listening on etcd-quickstart:2379Starting 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 | { |