帮你快速理解、总结文档立即下载

使用 HMAC Auth 认证访问

最近更新时间:2026-07-23 17:07:33

我的收藏

操作场景

本文介绍如何在 Kong 云原生网关上通过 HMAC Authentication 认证插件实现认证访问。

前置条件

已购买网关实例,详情请参见 新建网关
配置了服务(Service)以及路由(Route)。

操作步骤

本场景操作步骤以配置 Route 插件为例,指导如何实现 HMAC Auth 认证访问。

步骤1:配置 HMAC Authentication 认证插件

1. 登录 TSF 控制台
2. 在左侧导航栏选择云原生网关 ,单击目标实例进入实例详情。
3. 在基础信息页面,单击 Konga 控制台标签查看管理控制台登录方式。

4. 登录 Konga 管理控制台,进入需要限流的 Route 详情页,单击 Add Plugin 按钮创建插件,在 Authentication 分组下选择 HMAC Auth 插件。

5. 设置密钥身份认证插件参数,按 Enter 键,并保存。
consumer:填写需要应用访问控制的 Consumer ID,如空缺,表示该 IP 访问控制应用于所有 consumer。
hide credentials:是否隐藏来自上游服务的凭据。
anonymous:如果身份验证失败,则用作“匿名”使用者的可选字符串(使用者 uuid)值。如果为空(默认),则请求将失败,并且身份验证失败4xx。 请注意,此值必须引用 Kong 内部的 Consumer ID 属性,而不是其 custom_id。
clock skew:时钟偏移在几秒钟内以防止重放攻击。
validate request body:是否启用 body 验证。
enforce headers:客户端至少应用于 HTTP 签名创建的 header 列表。
algorithms:用户想要支持的 HMAC 摘要算法列表。允许的值为 hmac-sha1,hmac-sha256,hmac-sha384 和 hmac-sha512,默认全部支持。


步骤2:创建 Consumer

1. 进入 CONSUMERS 页面创建 Consumer。

username:用户(应用)名,必须指定此字段或 custom_id。
custom_id:用于将使用者映射到另一个数据库的自定义标识符。必须指定此字段或 username。
Tags:标签。

2. 为 Consumer 创建 HMAC Auth 凭证。

username:在 HMAC 签名验证中使用的用户名。
secret:secret 值,默认为空会自动生成 secret。


步骤3:签名认证方案

期望客户端使用以下参数化发送 Authorization 或 Proxy-Authorization header:
例如: Authorization: hmac username="username", algorithm="hmac-sha1", headers="x-date digest", signature="Base64(HMAC-SHA1(signing_str, secret))"

签名参数

username:凭证的 username。
algorithm:用于创建签名的数字签名算法。
headers:用于对请求进行签名的 HTTP 标头名称列表,由单个空格字符分隔。
signature:客户端生成的 Base64 编码数字签名。

签名字符串构造

为了生成使用密钥签名的字符串,客户端必须按照它们出现的顺序获取 headers 指定的每个 HTTP 标头的值。
1. 如果标题名称不是 request-line,则附加小写标题名称,后跟 ASCII 冒号:和 ASCII 空格 。
2. 如果标题名称是 request-line,则附加 HTTP 请求行(ASCII 格式),否则追加标题值。
3. 如果 value 不是最后一个值,则附加 ASCII 换行符\\n。字符串绝不能包含尾随的 ASCII 换行符。
4. 服务器和请求客户端应与 NTP 同步,并且应使用 X-Date 或 Date header 发送有效日期(使用 GMT 格式, 例如 Mon, 19 Mar 2018 12:08:40 GMT)。

正文验证 Body Validation

用户可以将 config.validate_request_body 设置为 true 以验证请求正文。如果启用,插件将计算请求正文的 SHA-256 HMAC 摘要,并将其与 Digest header 的值进行匹配。 摘要 header 需要采用以下格式: Digest: SHA-256=base64(sha256(<body>))
如果没有请求主体,则应将 Digest 设置为0长度的主体的摘要。
注意:
为了创建请求主体的摘要,插件需要将其保留在内存中,这可能会在处理大型主体(几个 MB)或高请求并发期间对工作者的 Lua VM 造成压力。

步骤4:API 请求 Demo

说明:
以下 Demo 仅供参考,针对 POST 请求和 GET 请求,请依据实际使用场景进行修改。
Python 版本
# -*- coding: utf-8 -*-
import base64
import datetime
import hashlib
import hmac
import json
import requests
from urllib.parse import urlparse, urlencode

#username
Username = 'xxx'
#secret
Secret = 'xxxx'

# 访问地址
Url = 'http://test.com/'
HTTPMethod = 'POST' # method
Accept = 'application/json'
ContentType = 'application/json'

urlInfo = urlparse(Url)
Host = urlInfo.hostname
Path = urlInfo.path

Digest = ''
GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
xDate = datetime.datetime.utcnow().strftime(GMT_FORMAT)

body_json = ''

# 修改 body 内容
if HTTPMethod == 'POST' :
# 填写实际请求body
body = { "arg1": "a", "arg2": "中文" }
body_json = json.dumps(body)

body_digest = hashlib.sha256(body_json.encode()).digest()
Digest = "SHA-256=" + base64.b64encode(body_digest).decode()

# 获取签名串
signing_str = 'x-date: %s\\ndigest: %s' % (
xDate, Digest)

# 计算签名
sign = hmac.new(Secret.encode(), msg=signing_str.encode(), digestmod=hashlib.sha1).digest()
sign = base64.b64encode(sign).decode()
auth = "hmac username=\\"" + Username + "\\", algorithm=\\"hmac-sha1\\", headers=\\"x-date digest\\", signature=\\""
sign = auth + sign + "\\""

# 发送请求
headers = {
'Host': Host,
'Accept': Accept,
'Content-Type': ContentType,
'x-date': xDate,
'Authorization': sign,
'Digest': Digest
}

if HTTPMethod == 'GET' :
ret = requests.get(Url, headers=headers)
if HTTPMethod == 'POST' :
ret = requests.post(Url, headers=headers, data=(body_json))

print(ret.headers)
print(ret.text)
Java 版本
package org.example;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class Main {
public static void main(String[] args) throws Exception {
String username = "xxxx";
String secret = "xxxx";
String url = "http://www.test.com/";
String httpMethod = "POST";
String accept = "application/json";
String contentType = "application/json";

URI uri = new URI(url);
String host = uri.getHost();

String digest = "";
SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
String xDate = format.format(new Date());
String body = "";
if (httpMethod.equals("POST")) {
body = "arg1=a&arg2=中文";
}
byte[] bodyDigest = java.security.MessageDigest.getInstance("SHA-256").digest(body.getBytes(StandardCharsets.UTF_8));
digest = "SHA-256=" + Base64.getEncoder().encodeToString(bodyDigest);
System.out.println(digest);

String signingStr = "x-date: " + xDate + "\\ndigest: " + digest;
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
byte[] signBytes = mac.doFinal(signingStr.getBytes(StandardCharsets.UTF_8));
String sign = Base64.getEncoder().encodeToString(signBytes);
String auth = "hmac username=\\"" + username + "\\", algorithm=\\"hmac-sha1\\", headers=\\"x-date digest\\", signature=\\"";
sign = auth + sign + "\\"";
HttpResponse response = null;
CloseableHttpClient httpClient = HttpClients.createDefault();
if (httpMethod.equals("POST")) {
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Host", host);
httpPost.setHeader("Accept", accept);
httpPost.setHeader("Content-Type", contentType);
httpPost.setHeader("x-date", xDate);
httpPost.setHeader("Authorization", sign);
if (!digest.isEmpty()) {
httpPost.setHeader("Digest", digest);
}
StringEntity entity = new StringEntity("arg1=a&arg2=中文", "UTF-8");
httpPost.setEntity(entity);
response = httpClient.execute(httpPost);
} else if (httpMethod.equals("GET")) {
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("Host", host);
httpGet.setHeader("Accept", accept);
httpGet.setHeader("Content-Type", contentType);
httpGet.setHeader("x-date", xDate);
httpGet.setHeader("Authorization", sign);
if (!digest.isEmpty()) {
httpGet.setHeader("Digest", digest);
}
response = httpClient.execute(httpGet);
}

HttpEntity responseEntity = response.getEntity();
System.out.println(EntityUtils.toString(responseEntity));
}
}

相关说明

更多相关说明请参见 Kong 插件