Skip to content

Commit

Permalink
feature: support consumer bind plugins. (#544)
Browse files Browse the repository at this point in the history
* feature: saved consumer plugin information.
* feature: supported to merge plugin with consumer and route.
  • Loading branch information
membphis authored Sep 21, 2019
1 parent 6b4d018 commit de3c95d
Show file tree
Hide file tree
Showing 19 changed files with 473 additions and 145 deletions.
58 changes: 55 additions & 3 deletions doc/architecture-design-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,20 +369,72 @@ APISIX 区别于其他 API 网关的一大特点是允许用户选择不同 Rout

如上图所示,作为 API 网关,需要知道 API Consumer(消费方)具体是谁,这样就可以对不同 API Consumer 配置不同规则。

|字段|必选|说明|
|---|----|----|
|username||Consumer 名称。|
|plugins||该 Consumer 对应的插件配置,它的优先级是最高的:Consumer > Route > Service。对于具体插件配置,可以参考 [Plugins](#plugin) 章节。|

在 APISIX 中,识别 Consumer 的过程如下图:

<img src="./images/consumer-internal.png" width="50%" height="50%">

1. 授权认证:比如有 [key-auth](doc/plugins/key-auth.md)[JWT](doc/plugins/jwt-auth-cn.md) 等。
1. 授权认证:比如有 [key-auth](./plugins/key-auth.md)[JWT](./plugins/jwt-auth-cn.md) 等。
2. 获取 consumer_id:通过授权认证,即可自然获取到对应的 Consumer `id`,它是 Consumer 对象的唯一识别标识。
3. 获取 Consumer 上绑定的 Plugin 或 Upstream 信息:完成对不同 Consumer 做不同配置的效果。

概括一下,Consumer 是某类服务的消费者,需与用户认证体系配合才能使用。
比如不同的 Consumer 请求同一个 API,网关服务根据当前请求用户信息,对应不同的 Plugin 或 Upstream 配置。

此外,大家也可以参考 [key-auth](doc/plugins/key-auth.md) 认证授权插件的调用逻辑,辅助大家来进一步理解 Consumer 概念和使用。
此外,大家也可以参考 [key-auth](./plugins/key-auth.md) 认证授权插件的调用逻辑,辅助大家来进一步理解 Consumer 概念和使用。

如何对某个 Consumer 开启指定插件,可以看下面例子:

```shell
# 创建 Consumer ,指定认证插件 key-auth ,并开启特定插件 limit-count
$ curl http://127.0.0.1:9080/apisix/admin/consumers/1 -X PUT -d '
{
"username": "jack",
"plugins": {
"key-auth": {
"key": "auth-one"
},
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
}'

# 创建 Router,设置路由规则和启用插件配置
$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -X PUT -d '
{
"plugins": {
"key-auth": {}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}'

# 发测试请求,前两次返回正常,没达到限速阈值
$ curl http://127.0.0.1:9080/hello -H 'apikey: auth-one' -I
...

*注意*:目前 APISIX 的 Consumer 还不支持绑定插件或上游信息,如果大家对这个功能点感兴趣,欢迎在社区中反馈交流。
$ curl http://127.0.0.1:9080/hello -H 'apikey: auth-one' -I
...

# 第三次测试返回 503,请求被限制
$ curl http://127.0.0.1:9080/hello -H 'apikey: auth-one' -I
HTTP/1.1 503 Service Temporarily Unavailable
...

```

[返回目录](#目录)

Expand Down
7 changes: 3 additions & 4 deletions doc/plugins/jwt-auth-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ curl http://127.0.0.1:9080/apisix/admin/consumers -X PUT -d '
"username": "jack",
"plugins": {
"jwt-auth": {
"key": "your-consumer-key",
"secret": "secret-key"
"key": "user-key",
"secret": "my-secret-key"
}
}
}'
Expand Down Expand Up @@ -141,8 +141,7 @@ $ curl http://127.0.0.1:2379/v2/keys/apisix/routes/1 -X PUT -d value='
"methods": ["GET"],
"uri": "/index.html",
"id": 1,
"plugins": {
},
"plugins": {},
"upstream": {
"type": "roundrobin",
"nodes": {
Expand Down
2 changes: 1 addition & 1 deletion doc/plugins/key-auth-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ curl http://127.0.0.1:9080/apisix/admin/consumers -X PUT -d '
"username": "jack",
"plugins": {
"key-auth": {
"key": "keykey"
"key": "auth-one"
}
}
}'
Expand Down
4 changes: 2 additions & 2 deletions doc/plugins/key-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ Two steps are required:
1. creates a consumer object, and set the attributes of plugin `key-auth`.

```shell
curl http://127.0.0.1:9080/apisix/admin/consumers -X PUT -d '
curl http://127.0.0.1:9080/apisix/admin/consumers -X PUT -d '
{
"username": "jack",
"plugins": {
"key-auth": {
"key": "keykey"
"key": "auth-one"
}
}
}'
Expand Down
13 changes: 11 additions & 2 deletions lua/apisix.lua
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ function _M.http_access_phase()
core.table.clear(plugins)
api_ctx.plugins = plugin.filter(global_rule, plugins)
run_plugin("rewrite", plugins, api_ctx)
run_plugin("access", plugins, api_ctx)
end

core.tablepool.release("plugins", plugins)
Expand Down Expand Up @@ -298,7 +299,7 @@ function _M.http_access_phase()
end

local changed
route, changed = plugin.merge_route(service, route)
route, changed = plugin.merge_service_route(service, route)
api_ctx.matched_route = route

if changed then
Expand Down Expand Up @@ -338,6 +339,14 @@ function _M.http_access_phase()
api_ctx.plugins = plugin.filter(route, plugins)

run_plugin("rewrite", plugins, api_ctx)
if api_ctx.consumer then
local changed
route, changed = plugin.merge_consumer_route(route, api_ctx.consumer)
if changed then
core.table.clear(api_ctx.plugins)
api_ctx.plugins = plugin.filter(route, api_ctx.plugins)
end
end
run_plugin("access", plugins, api_ctx)
end

Expand Down Expand Up @@ -375,7 +384,7 @@ function _M.grpc_access_phase()
end

local changed
route, changed = plugin.merge_route(service, route)
route, changed = plugin.merge_service_route(service, route)
api_ctx.matched_route = route

if changed then
Expand Down
31 changes: 23 additions & 8 deletions lua/apisix/admin/consumers.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
local core = require("apisix.core")
local core = require("apisix.core")
local plugins = require("apisix.admin.plugins")
local plugin = require("apisix.plugin")
local pairs = pairs

local _M = {
version = 0.1,
Expand All @@ -24,13 +26,26 @@ local function check_conf(consumer_name, conf)
return nil, {error_msg = "invalid configuration: " .. err}
end

if not conf.plugins then
return consumer_name
end

ok, err = plugins.check_schema(conf.plugins)
if not ok then
return nil, {error_msg = "invalid configuration: " .. err}
if conf.plugins then
ok, err = plugins.check_schema(conf.plugins)
if not ok then
return nil, {error_msg = "invalid plugins configuration: " .. err}
end

local count_auth_plugin = 0
for name, conf in pairs(conf.plugins) do
local plugin_obj = plugin.get(name)
if plugin_obj.type == 'auth' then
count_auth_plugin = count_auth_plugin + 1
if count_auth_plugin > 1 then
return nil, {error_msg = "only one auth plugin is allowed"}
end
end
end

if count_auth_plugin == 0 then
return nil, {error_msg = "require one auth plugin"}
end
end

return consumer_name
Expand Down
65 changes: 24 additions & 41 deletions lua/apisix/consumer.lua
Original file line number Diff line number Diff line change
@@ -1,38 +1,16 @@
local lrucache = require("apisix.core.lrucache")
local schema = require("apisix.core.schema")
local config = require("apisix.core.config_etcd")
local insert_tab = table.insert
local core = require("apisix.core")
local plugin = require("apisix.plugin")
local error = error
local ipairs = ipairs
local pairs = pairs
local consumers
local error = error
local ipairs = ipairs
local pairs = pairs


local _M = {
version = 0.1,
version = 0.2,
}

--[[
{
"id": "ShunFeng",
"plugins": {
"key-auth": {
"key": "dddxxyyy"
}
}
}

to
{
key-auth: [
{
"key": "dddxxyyy",
"consumer_id": "ShunFeng"
}
]
}
]]
local function plugin_consumer()
local plugins = {}

Expand All @@ -41,17 +19,23 @@ local function plugin_consumer()
end

for _, consumer in ipairs(consumers.values) do
for name, conf in pairs(consumer.value.plugins) do
if not plugins[name] then
for name, config in pairs(consumer.value.plugins or {}) do
local plugin_obj = plugin.get(name)
if plugin_obj and plugin_obj.type == "auth"
and not plugins[name] then
plugins[name] = {
nodes = {},
conf_version = consumers.conf_version,
conf_version = consumers.conf_version
}
end

local new_consumer = core.table.clone(consumer.value)
new_consumer.consumer_id = new_consumer.id
new_consumer.auth_conf = config
core.log.info("consumer:", core.json.delay_encode(new_consumer))
core.table.insert(plugins[name].nodes, new_consumer)

insert_tab(plugins[name].nodes,
{consumer_id = consumer.value.id, conf = conf})
break
end
end
end

Expand All @@ -60,19 +44,18 @@ end


function _M.plugin(plugin_name)
local plugin_conf = lrucache.global("/consumers",
consumers.conf_version, plugin_consumer)
local plugin_conf = core.lrucache.global("/consumers",
consumers.conf_version, plugin_consumer)
return plugin_conf[plugin_name]
end


function _M.init_worker()
local err
consumers, err = config.new("/consumers",
{
automatic = true,
item_schema = schema.consumer
})
consumers, err = core.config.new("/consumers", {
automatic = true,
item_schema = core.schema.consumer
})
if not consumers then
error("failed to create etcd instance for fetching consumers: " .. err)
return
Expand Down
1 change: 0 additions & 1 deletion lua/apisix/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,5 @@ return {
utils = require("apisix.core.utils"),
etcd = require("apisix.core.etcd"),
http = require("apisix.core.http"),
consumer = require("apisix.consumer"),
tablepool= require("tablepool"),
}
Loading

0 comments on commit de3c95d

Please sign in to comment.