前言
以前我们使用的是zuul 然后实现了一些filter 所以,功能很简单,而且 性能也不太高,借着这次的机会,我负责架构的中间件,重新选型并使用一些主流的组件。
一 API gateway 主要功能
1.1 动态路由配置
1.2 灰度发布
1.2.1 接口版本控制
1.2.2 小程序、app 审核灰度
1.2.3 app升级,指定新版本
1.2.4 app不升级,指定新版本
1.3 限流
1.3.1 sentinel
1.3.2 网关限流
1.4 黑白名单
- 黑名单禁止
- 白名单放行
- 白名单需要授权的走授权
1.5 鉴权
统一授权认证
接口参数签名1.5.1 接口签名
1.5.2 oauth2.0
1.6 监控
1.6.1 SkyWalking Trace 监控
1.7 test
聚合swagger
二 kong
2.1 安装部署
2.1.1 安装部署架构图
2.1.2 docker 安装
2.2 汉化
利用第三方个人实现的语言包,进行替换。
docker stop kong_konga_1 #停止正在运行的容器docker cp kong_konga_1:/app/assets ./ #将容器的文件复制到本地docker pull jsonljd/konga-lang-plugin:latest #拉取语言插件镜像docker run -d --name konga-lang-plugin -v /Users/hezhaoming/Documents/docker/kong/assets:/app/assets jsonljd/konga-lang-plugindocker cp ./assets kong_konga_1:/app/ #覆盖成功后即可docker start kong_konga_1 #重启容器
2.3 konga 基本使用
2.3.1 upstream&target
首先需要新建上游,也就是我们反向代理的地址(我们的目标地址)。
- step1 创建上游

- step 创建管理target

- step 创建管理target
2.3.2 server
2.3.3 route
2.3.4 执行过程原理
2.4 高级使用
2.4.1 网关监控
2.4.2 日志监控
2.4.3 限流
2.4.4 熔断重试
2.4.5 鉴权
2.4.6 接口参数签名
接口安全三要素:合法身份、参数防篡改、防重放攻击(请求唯一)。展开来说分三步:
- 合法身份:
- 我们为开发者分配 AccessKey & Secretkey,即给第三方app调用方,分配一个合法的身份。
- 参数防篡改:
- 我们拼接字符串:str=MD5取摘要(stringA(key1=value1&key2=value2…)+Secretkey+timestap)
- sha(str,secret)加密
- 防重放攻击:
- timestamp+nonce:我们为没一个请求生成唯一key,即防止重复的请求,此外,根据时间戳,定期清除无用的nonce(我们可以利用redis的过期策略实现)。
上面说的签名验证方式,市面有很多实现,也可以自己手撸一个也无妨(我之前公司都是手撸的一套java+react),这里主要讨论的是kong的HAMC签名认证组件。
2.4.6.1 合法身份
前面说了,我们首先要为调用者APP 分配 accessKey ,Secretkey。
setp1. 新建api消费者
api创建方式:
curl -i -X POST http://localhost:8001/consumers/ \
-d "username=alice"
setp2. 为第三方应用app分配 key、secre
api创建方式:
curl -i -X POST http://localhost:8001/consumers/alice/hmac-auth \
-d "username=alice123" \
-d "secret=secret"
2.4.6.2 为服务新建HMAC认证
注意:新增的签名认证填写任何参数
api创建方式:
curl -i -X POST http://localhost:8001/services/jk-member-svc2/plugins \
-d "name=hmac-auth" \
2.4.6.3 签名算法
这里涉及到两个算法,md5获取摘要,然后sha1加密,这里有个面试题:为啥不用原生的字符串加密,两个原因1. MD5摘要算法,长度固定,2. 不是明文显示保证安全性。
- MD5算法 ```java package cn.sephora;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils;
import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException;
/**
- @author hezhaoming
- @date 2020/11/23
*/
public class Md5Util {
public static String md5(String param) {
} } ```if (StringUtils.isBlank(param)) { throw new IllegalArgumentException("param can not be null"); } try { byte[] bytes = param.getBytes("utf-8"); final MessageDigest md = MessageDigest.getInstance("MD5"); md.reset(); md.update(bytes); final Base64 base64 = new Base64(); final byte[] enbytes = base64.encode(md.digest()); return new String(enbytes); } catch (final NoSuchAlgorithmException e) { throw new IllegalArgumentException("unknown algorithm MD5"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); }
- HmacSha1Util算法 ```java package cn.sephora;
import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Formatter;
/**
- @author hezhaoming
@date 2020/11/23 */ public class HmacSha1Util { private static final String HMAC_SHA1_ALGORITHM = “HmacSHA1”;
private static String toHexString(byte[] bytes) {
Formatter formatter = new Formatter(); for (byte b : bytes) { formatter.format("%02x", b); } return formatter.toString();}
public static String signature(String data, String key) throws NoSuchAlgorithmException, InvalidKeyException {
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM); Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM); mac.init(signingKey); return toHexString(mac.doFinal(data.getBytes()));}
public static byte[] signatureReturnBytes(String data, String key) throws NoSuchAlgorithmException, InvalidKeyException {
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM); Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM); mac.init(signingKey); return mac.doFinal(data.getBytes());}
}
3. HmacTest
```java
package cn.sephora;
import lombok.extern.slf4j.Slf4j;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* @author hezhaoming
* @date 2020/11/23
*/
@Slf4j
public class HmacTest {
public static void main(String[] args) {
test1();
}
static void test1() {
String queryParam = "name=122&password=123";
String contentMD5 = Md5Util.md5(queryParam);
log.info("content-md5: {}", contentMD5);
Date d = new Date();
DateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
String headerDate = format.format(d);
log.info("x-date: {}", headerDate);
StringBuilder stb = new StringBuilder();
String content = stb.append("x-date: ").append(headerDate).append("\n").append("content-md5: ").append(contentMD5).toString();
log.info("签名前内容: " + content);
String secret = "secret"; //用户yanyuylou的密钥
try {
String signature2 = new String(Base64.getEncoder().encode(HmacSha1Util.signatureReturnBytes(content, secret)), "US-ASCII");
log.info("显示指定编码[推荐]: {}", signature2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.4.6.4 测试demo
header 三个值
Content-md5 tkygbxlPERf+/tZghlMlXw==
X-Date Mon, 23 Nov 2020 06:44:19 GMT
Authorization Authorization: hmac username="alice123", algorithm="hmac-sha1", headers="x-date content-md5", signature="dojzuTSR3W/nhNJ+A9SY9+pJKTo="

api 测试方式
curl -i -X GET http://localhost/api2/user/getById/1 \
-H "Content-md5: Qnwej0d5uZ9zgi34yYcoaw==" \
-H "x-date: Mon, 23 Nov 2020 03:45:05 GMT" \
-H 'Authorization: hmac username="alice123", algorithm="hmac-sha1", headers="x-date content-md5", signature="+Ro8wRWnVYdfvkZ8l4FFFPauBwE="'
2.4.7 黑白名单
2.4.8 灰度发布
2.4.9 请求协议转化
request
response


