Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: support consumer bind plugins. #544

Merged
merged 22 commits into from
Sep 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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