3.8.0-Kong网关自定义插件-增强版CORS跨域插件
能力增加:
1、origins支持正则、不需要指定协议、不需要指定端口
例如:*.demo.com:*
2、增强安全性,不允许非指定的主机名访问(返回403),kong自带的插件也可以,但必须写一堆清单,支持正则后就方便多了
1、新建自定义插件目录
mkdir -p /usr/local/share/lua/5.1/kong/plugins/dynamic-cors/ && chown -R kong.kong /usr/local/share/lua/5.1/kong/plugins/dynamic-cors
2、新建Lua文件
init.lua
local DynamicCorsHandler = require("kong.plugins.dynamic-cors.handler")
return {
name = "dynamic-cors",
version = "1.0.0",
handler = DynamicCorsHandler,
schema = require("kong.plugins.dynamic-cors.schema"),
priority = 900,
}
config.lua
return {
no_consumer = true,
name = "dynamic-cors",
fields = {
config = {
type = "record",
fields = {}
}
}
}
schema.lua
local typedefs = require "kong.db.schema.typedefs"
return {
name = "dynamic-cors",
fields = {
{ consumer = typedefs.no_consumer },
{ protocols = typedefs.protocols_http },
{ config = {
type = "record",
fields = {
{ origins = {
type = "array",
default = { "*" },
elements = { type = "string" },
description = "允许的域名列表,支持通配符如 *.example.com"
}},
{ methods = {
type = "array",
default = { "*" },
elements = { type = "string" },
description = "允许的 HTTP 方法"
}},
{ headers = {
type = "array",
default = { "*" },
elements = { type = "string" },
description = "允许的请求头"
}},
{ exposed_headers = {
type = "array",
default = {},
elements = { type = "string" },
description = "允许客户端访问的响应头"
}},
{ credentials = {
type = "boolean",
default = true,
description = "是否允许发送 Cookie/认证信息"
}},
{ max_age = {
type = "integer",
default = 3600,
description = "预检请求缓存时间(秒)"
}},
{ allow_wildcard_subdomains = {
type = "boolean",
default = true,
description = "是否启用通配符子域名匹配"
}},
{ origin_regex_patterns = {
type = "array",
default = {},
elements = { type = "string" },
description = "正则表达式模式列表"
}},
{ debug = {
type = "boolean",
default = false,
description = "是否启用调试日志"
}}
}
}}
}
}
handler.lua
local DynamicCorsHandler = { PRIORITY = 999, VERSION = "1.5" }
local function safe_wildcard_match(origin, pattern)
-- 1. 完全匹配
if origin == pattern then
return true
end
-- 2. 全局通配符
if pattern == "*" then
return true
end
-- 3. 提取 Origin 的主机名
local origin_host
if origin:find("://") then
local rest = origin:match("://(.+)$")
if rest then
origin_host = rest:match("^([^:]+)")
end
else
origin_host = origin:match("^([^:]+)")
end
if not origin_host then
return false
end
-- 4. 处理带端口通配符的模式
if pattern:find(":*$") then
local host_pattern = pattern:sub(1, -3)
if host_pattern == "*" then
return true
end
-- 安全匹配逻辑:必须以 .domain 结尾
if host_pattern:sub(1, 2) == "*." then
local domain = host_pattern:sub(3)
if origin_host == domain or origin_host:sub(-#domain - 1) == "." .. domain then
if origin_host ~= domain then
return true
end
end
end
if origin_host == host_pattern then
return true
end
end
-- 5. 处理不带端口通配符的模式
if pattern:sub(1, 2) == "*." then
local domain = pattern:sub(3)
if origin_host == domain or origin_host:sub(-#domain - 1) == "." .. domain then
if origin_host ~= domain then
return true
end
end
end
-- 6. 直接主机名匹配
if origin_host == pattern then
return true
end
return false
end
function DynamicCorsHandler:access(conf)
local origin = kong.request.get_header("Origin")
if origin then
if conf.origins and #conf.origins > 0 then
local found_match = false
for _, pattern in ipairs(conf.origins) do
if safe_wildcard_match(origin, pattern) then
found_match = true
break
end
end
if not found_match then
return kong.response.exit(403, "Cross-Origin Request Blocked")
end
end
end
end
function DynamicCorsHandler:header_filter(conf)
local origin = kong.request.get_header("Origin")
if origin then
local should_set_cors = false
if not conf.origins or #conf.origins == 0 then
should_set_cors = true
else
for _, pattern in ipairs(conf.origins) do
if safe_wildcard_match(origin, pattern) then
should_set_cors = true
break
end
end
end
if should_set_cors then
-- 基础 CORS 头
kong.response.set_header("Access-Control-Allow-Origin", origin)
kong.response.set_header("Vary", "Origin")
if conf.credentials then
kong.response.set_header("Access-Control-Allow-Credentials", "true")
end
if conf.exposed_headers and #conf.exposed_headers > 0 then
local exposed_str
if type(conf.exposed_headers) == "table" then
exposed_str = table.concat(conf.exposed_headers, ", ")
else
exposed_str = conf.exposed_headers
end
kong.response.set_header("Access-Control-Expose-Headers", exposed_str)
end
-- 获取当前请求方法
local request_method = kong.request.get_method()
-- 如果是 OPTIONS 请求,设置预检头
if request_method == "OPTIONS" then
local methods
if conf.methods then
if type(conf.methods) == "table" and #conf.methods > 0 then
methods = table.concat(conf.methods, ", ")
elseif conf.methods ~= "" then
methods = conf.methods
else
methods = "*"
end
else
methods = "*"
end
kong.response.set_header("Access-Control-Allow-Methods", methods)
local headers
if conf.headers then
if type(conf.headers) == "table" and #conf.headers > 0 then
headers = table.concat(conf.headers, ", ")
elseif conf.headers ~= "" then
headers = conf.headers
else
headers = "*"
end
else
headers = "*"
end
kong.response.set_header("Access-Control-Allow-Headers", headers)
-- 缓存时间
kong.response.set_header("Access-Control-Max-Age", conf.max_age or "86400")
end
-- 删除敏感头信息
if conf.remove_headers and #conf.remove_headers > 0 then
for _, header in ipairs(conf.remove_headers) do
kong.response.clear_header(header)
end
end
end
end
end
return DynamicCorsHandler
dynamic-cors-1.0.0-1.rockspec
package = "dynamic-cors"
version = "1.0.0-1"
source = {
url = "git://localhost/dynamic-cors"
}
description = {
summary = "Dynamic CORS Plugin for Kong with wildcard support",
detailed = [[
A Kong plugin that provides flexible CORS support with wildcard domains,
regex patterns, and dynamic origin reflection.
]],
homepage = "https://github.com/your-org/kong-dynamic-cors",
license = "MIT"
}
dependencies = {
"lua >= 5.1",
"kong >= 3.8.0"
}
build = {
type = "builtin",
modules = {
["kong.plugins.dynamic-cors.handler"] = "handler.lua",
["kong.plugins.dynamic-cors.schema"] = "schema.lua"
}
}
3、在配置文件里将自定义插件加上
/etc/kong/kong.conf
plugins = bundled,dynamic-cors
4、可以通过KongA可视化配置,也可以用命令



浙公网安备 33010602011771号