为了保证通知的安全,系统提供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方式)。