为了保证通知的安全,系统提供2种方式供用户选择:IP白名单模式和签名验签模式。 两种模式可以单独使用,也可以共同使用。
一、IP白名单模式
为防止回调接口被攻击,可使用IP白名单的机制进行访问限制。
所有e签宝系统回调通知的IP地址:
测试环境:47.96.79.204
正式环境:118.31.35.8
以下以Nginx服务器为例,介绍如何配置e签宝白名单
1、新建白名单 ip_white.conf
新建文件ip_white.conf,内容如下:
allow 47.96.79.204; allow 118.31.35.8;
2、nginx.conf 配置示例
#geoIP的白名单设置
geo $remote_addr $ip_whitelist {
default 0;
include ip_white.conf;
}
location /console {
proxy_redirect off;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
if ( $ip_whitelist = 1 ) {
proxy_pass http://127.0.0.1:8000;
break;
}
return 403;
}3、java获取回调请求IP示例
public static String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个ip值,第一个ip才是真实ip
if( ip.indexOf(",")!=-1 ){
ip = ip.split(",")[0];
}
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}二、签名验签模式
在回调客户服务时,在请求header里面系统新增四个参数
参数名称 | 说明 | 参数类型 |
X-Tsign-Open-App-Id | 客户发生业务时的项目ID | header |
X-Tsign-Open-SIGNATURE | 签名值 | header |
X-Tsign-Open-TIMESTAMP | 时间戳 | header |
X-Tsign-Open-SIGNATURE-ALGORITHM | 使用的算法, 默认算法hmac-sha256 | header |
以算法hmac-sha256签名验证做说明,验签的数据有四部分
- 1、时间戳
回调header的X-Tsign-Open-TIMESTAMP
- 2、query请求的数据
客户设置的回调地址可能包含query数据,例如callback?accountId=aaa&orderNo=001。(e签宝平台不会追加任何参数)
- 3、body 数据
即通知实际内容,按照整体的字节流来处理
- 4、项目ID对应的密钥
客户在e签宝开放平台生成项目ID时对应的密钥
.NET示例
public static void notify()
{
//异步通知请求地址 "notifyUrl": "http://saledemo.tsign.cn:9090/asyn/notify?belong=tianyin",
//异步通知获取到的签名值
string signture = "xxxxxx31063db9f5e70f391445480b112f66ac728f19ff11755";
//1578384199851:header头中的时间戳x-tsign-open-timestamp;
tianyin:是异步请求地址拼接的请求参数值,如果请求地址没有?拼接参数,则只填写时间戳
string a = "xxxxx51";
//密钥
string secret = "xxxx4d8f922b898ac519b4c f";
// body体请求参数
string data = "{\"flowId\":\"xxxx8926460290884\",\"success\":true,\"contextId\":\"xxxd-c5f9-4652-a053-1130d86c8fa8\",\"verifycode\":\"xxx240623371c84becdc\",\"serviceId\":\"xxxx290884\",\"status\":true}";
//最终参与验签的请求参数
string data1 = a + data;
//计算签名方法
string mysign = GetSignature(data1, secret);
string MYSIGN = mysign.ToLower();
Console.Write("mysign=" + mysign);
if (MYSIGN.Equals(signture))
{
MessageBox.Show("验签成功");
}
else
{
MessageBox.Show("验签失败");
}
}
public static string GetSignature(string data, string secret)
{
byte[] keyByte = Encoding.UTF8.GetBytes(secret);
byte[] messageBytes = Encoding.UTF8.GetBytes(data);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
StringBuilder sb = new StringBuilder();
foreach (byte test in hashmessage)
{
sb.Append(test.ToString("X2"));
}
return sb.ToString();
}
}注意:实名认证接口 - 选择刷脸方式,完成认证后会多推送一个body为空的get方式异步回调,导致客户验签不通过导致报错,建议是异步通知新增判断,只接收POST请求的回调(过滤掉GET方式)。