有奖捉虫:行业应用 & 管理与支持文档专题 HOT

操作场景

如果 API 网关提供的认证鉴权方式不能满足您的需求,您可以使用自定义认证插件,通过您自定义的代码进行认证鉴权。
自定义认证插件作用在请求过程中,客户端请求 API 网关后,API 网关会将请求内容转发到认证函数中。您可以将认证函数部署在云函数上,公网、或内网 VPC 上,认证通过后请求才会被转发给业务后端,否则将拒绝请求。




前提条件

对于部署在云函数的认证服务,需开通 云函数 服务。
对于部署在公网/内网的服务,需要按照最下方的代码模板搭建对应的认证服务。

操作步骤

步骤1:创建认证函数

对于部署在公网或内网 VPC 的认证函数,可省略该步骤。
1. 登录 云函数控制台
2. 在左侧导航栏,单击函数服务,进入函数列表页。
3. 单击页面左上角的新建,选择模板创建,可搜索认证类函数模板,如图模糊搜索自定义认证、选择标签为 Python2.7 的版本。



4. 模板默认提供了三种参数认证的示例:Header、Query、Body。实际业务中,您可以根据需要选择不同的认证方式,也可以结合多个参数进行认证。




步骤2:创建自定义认证插件

1. 登录 API 网关控制台
2. 在左侧导航栏,单击插件 > 自定义插件,进入自定义插件列表页。
3. 单击页面左上角的新建,新建一个自定义认证插件。
对于部署在云函数的认证服务,创建自定义认证插件时需要填写的数据如下:
参数
是否必填
说明
选择函数
必填
选择认证函数所在的命名空间、名称和版本。
后端超时
必填
设置 API 网关转发到认证函数的后端超时时间,超时时间的最大限制为30分钟。在 API 网关调用认证函数,未在超时时间内获得响应时,API 网关将终止此次调用,并返回相应的错误信息。
是否发送 Body
必填
当此选项的值为“是”时,会把客户端请求的 Header、Body、Query 都会发送给云函数。
当此选项的值为“否”时,不会发送请求 Body。
认证参数
选填
选填,但如果设置了参数则请求数据中为必填。当“缓存时间”不为0时,必须设置此参数。使用缓存时,此参数将作为搜索条件来查询认证结果。
缓存时间
必填
设置认证结果缓存的时间。值为0时代表不开启缓存,缓存时间最大支持3600秒。
对于部署在公网的认证服务,创建自定义认证插件时需要填写的数据如下:
参数
是否必填
说明
请求方法
必填
请求自定义认证函数的方法,支持 GET、POST、PUT、DELETE、HEAD、ANY。
公网服务
必填
自定义认证服务访问地址,支持 HTTP 和 HTTPS 协议。
路径匹配模式
必填
支持后端路径匹配和全路径匹配两种方式。
后端路径匹配:直接使用配置的路径请求服务。
全路径匹配:使用去除请求路径的路径请求服务,如 API 路径配置为 /a/,请求路径为 /a/b,开启全路径匹配后,传输给服务的为 /b。
请求数据格式
必填
APIGW会把客户端发送的请求按照不同的格式转发给公网/内网认证服务。
原始格式:按照请求原始的内容和格式转发给认证服务。
JSON:将原始请求中的 Header Query Body Method PATH 参数转换成新的格式作为 Body 转发给公网/内网认证服务。
超时时间
必填
请求后端超时时间。默认15s。
是否发送body
必填
如果后端认证服务不需要通过 Body 做额外验证,可以不需要发送 Body,减少转发的性能损耗。
缓存时间
必填
相同的认证参数在缓存时间内只会请求一次后端。默认为0,表示不走缓存,每次都请求后端认证服务。
认证参数
选填
选填,但如果设置了参数则请求数据中为必填。当“缓存时间”不为0时,必须设置此参数。使用缓存时,此参数将作为搜索条件来查询认证结果。



对于部署在内网 VPC 的认证服务,创建自定义认证插件时需要填写的数据如下:
参数
是否必填
说明
选择 VPC
必填
选择认证服务所属的 VPC。
请求方法
必填
请求自定义认证函数的方法,支持 GET、POST、PUT、DELETE、HEAD、ANY。
后端地址
必填
自定义认证服务访问地址,支持 HTTP 和 HTTPS 协议。




步骤3:绑定 API

1. 在列表中选中刚刚创建好的插件,单击操作列的绑定 API
2. 在绑定 API 弹窗中选择服务和环境,并选择需要绑定插件的 API。



3. 单击确定,即可将插件绑定到 API,此时插件的配置已经对 API 生效。

插件及请求响应示例

请在下方示例区切换页签,查看插件数据 pluginData、请求示例、响应示例。
插件数据
请求认证服务(JSON格式)
返回响应(application/JSON格式)
{
"cache_time":10, // 认证结果缓存时间,单位秒,合法值:0 ~ 3600
"endpoint_timeout":15, // 后端超时时间,单位秒,合法值:0 ~ 60
"func_name":"test_name", // 自定义函数名称
"func_namespace":"test_namespace", // 自定义函数命名空间
"func_qualifier":"$LATEST", // 自定义函数版本
"is_send_body":true, // 是否将请求Body发送到函数
"header_auth_parameters":[ // Header位置的认证参数,插件根据参数值来缓存认证结果
"Header1"
],
"query_auth_parameters":[ // Query位置的认证参数,插件根据参数值来缓存认证结果
"Query1"
],
"user_id":1253970226, // appid
"version":"2021-12-26 17:17:49"
// 插件版本,格式:yyyy-MM-dd HH:mm:ss,编辑插件时,传入新值会使得插件下的缓存结果失效
}
{ "requestContext": { // 请求的信息 "path": "\\/test", "httpMethod": "POST", "sourceIp": "113.108.77.64", "stage": "release", "serviceId": "service-i76yjnfu", "identity": {} }, "path": "\\/test", // 请求路径 "httpMethod": "POST", // 请求方法 "body": "{\\"data\\":1}", // 请求Body "queryStringParameters": { // 请求的Query "query": "1"}, "headerParameters": { // 请求Header "accept": "*\\/*", "host": "service-ixxxxx-xxxxx.gz.tencentapigw.cn", "header-auth": "apigw", "content-length": "10", "content-type": "application\\/x-www-form-urlencoded", "user-agent": "curl\\/8.1.2" }
{ "api-auth": true, // 认证结果 true:认证通过 false:认证失败 "api-succ-headers": {"key":"value"}, // 认证成功后需要添加到转发到后端的header "api-response": "auth fail" // 认证失败后返回自定义内容 }

注意事项

当用户开启缓存并配置了认证参数时,API 网关会进行参数校验。如果请求不传递该认证参数,API 网关将会报错“缺少 xxx 参数”。API 网关做参数校验和命中缓存时,都是大小写不敏感的。
每次将自定义插件绑定到一个网关 API 时,相当于为认证函数创建了一个该网关 API 的触发器。在 SCF 侧删除触发器,相当于把插件和 API 解绑。
自定义认证插件目前仅支持事件函数,不支持 Web 函数。
自定义认证插件可与 API 网关提供的认证方式共存,API 网关提供的认证方式优先级高于自定义认证,建议您将自定义认证插件绑定的 API 网关 API 设置为“免认证”。

自定义服务类型规则

自定义插件的自定义服务类型,支持绑定不同实例上的服务 API,规则请参见 插件概述-已支持自定义插件类型

示例代码:自定义认证服务

以下均以 Python2.7 为例,提供2种内网或公网的数据请求格式的示例。
原始数据格式
JSON请求格式
from http.server import BaseHTTPRequestHandler, HTTPServer from urllib.parse import urlparse, parse_qs import json PORT_NUMBER = 8088 class MyHandler(BaseHTTPRequestHandler): def do_POST(self): response = {'api-auth': True} # Header认证 header_auth_key = self.headers.get("header-auth") if header_auth_key == "apigw": response['api-auth'] = False # Query认证 # parsed_url = urlparse(self.path) # query_params = parse_qs(parsed_url.query) # query_auth_key = query_params.get("query-auth", [None])[0] #if query_auth_key == "apigw": # response['api-auth'] = False self.send_response(200) self.send_header('Content-type', 'application/json') self.end_headers() self.wfile.write(json.dumps(response).encode('utf-8')) try: server = HTTPServer(('', PORT_NUMBER), MyHandler) print(f'Started HTTP server on port {PORT_NUMBER}') # Wait forever for incoming HTTP requests server.serve_forever() except KeyboardInterrupt: print('^C received, shutting down the web server') server.socket.close()
from http.server import BaseHTTPRequestHandler, HTTPServer import json PORT_NUMBER = 8088 class MyHandler(BaseHTTPRequestHandler): def do_POST(self): response = {'api-auth': True} content_length = int(self.headers.get('Content-Length', 0)) request_body = self.rfile.read(content_length).decode('utf-8') request_body = json.loads(request_body) # Header认证 header_parameters = request_body.get('headerParameters', {}) if header_parameters.get('header-auth') == "apigw": response['api-auth'] = False # Query认证 # query_parameters = request_body.get('queryStringParameters', {}) # if query_parameters.get('query-auth') == "apigw": # response['api-auth'] = False self.send_response(200) self.send_header('Content-type', 'application/json') self.end_headers() self.wfile.write(json.dumps(response).encode('utf-8')) try: server = HTTPServer(('', PORT_NUMBER), MyHandler) print(f'Started HTTP server on port {PORT_NUMBER}') # Wait forever for incoming HTTP requests server.serve_forever() except KeyboardInterrupt: print('^C received, shutting down the web server') server.socket.close()

常见错误信息

错误信息
含义
Custom authentication failed: authentication information, incorrect
认证失败,默认返回内容。
Custom authentication failed: custom service request, request failed
请求后端服务失败,需排查后端服务状态是否正常。
Custom authentication failed: custom service response, invalid content
认证服务返回的数据格式错误或者认证服务返回的数据做了 Gzip 压缩,当前不支持对认证服务响应做 Gzip 数据解析。