快速导航
[异常现象]
异常现象[异常原因]
异常原因[解决方法]
解决方法[参考资料]
参考资料
SpringBoot整合Shiro骨架采用的是FEBS-Shiro, FEBS-Shiro整合银联支付,支付骨架采用的是IJPay,感谢两位作者的开源。
\*\*
IJPay最新版本银联支付已修改为云闪付,这里采用的是V2.2.0版本,还是采用的网关支付方式。
\*\*
附上Gitee源码地址,如果帮助到您,还请给个Star。代码示例
异常现象
代码示例
直接使用IJPay中提供的代码
/**
* 后台回调
*/
@RequestMapping(value = "/backRcvResponse", method = { RequestMethod.POST, RequestMethod.GET })
@ResponseBody
public String backRcvResponse(HttpServletRequest request) {
logger.info("BackRcvResponse接收后台通知开始,Content-Type:{}", request.getContentType());
String encoding = "UTF-8";
String notifyStr = readData(request);
// 获取银联通知服务器发送的后台通知参数
Map reqParam = getAllRequestParamToMap(notifyStr);
LogUtil.printRequestLog(reqParam);
// 重要!验证签名前不要修改reqParam中的键值对的内容,否则会验签不过
if (!AcpService.validate(reqParam, encoding)) {
logger.info("后台验证签名结果[失败].");
// 验签失败,需解决验签问题
} else {
logger.info("后台验证签名结果[成功].");
// 【注:为了安全验签成功才应该写商户的成功处理逻辑】交易成功,更新商户订单状态
String orderId = reqParam.get("orderId"); // 获取后台通知的数据,其他字段也可用类似方式获取
String respCode = reqParam.get("respCode");
// 判断respCode=00、A6后,对涉及资金类的交易,请再发起查询接口查询,确定交易成功后更新数据库。
logger.info("orderId>>>" + orderId + " respCode>>" + respCode);
}
logger.info("BackRcvResponse接收后台通知结束");
// 返回给银联服务器http 200 状态码
return "ok";
}
控制台日志
上述代码执行结果日志
2020-06-13 16:52:46 INFO http-nio-8080-exec-8 cc.mrbird.febs.pay.unionpay.controller.UnionPayController BackRcvResponse接收后台通知开始,Content-Type:application/x-www-form-urlencoded; charset=UTF-8
2020-06-13 16:52:46 INFO http-nio-8080-exec-8 cc.mrbird.febs.pay.unionpay.controller.UnionPayController 收到通知报文:
2020-06-13 16:52:46 INFO http-nio-8080-exec-8 SDK_MSG_LOG ============================== SDK REQ MSG BEGIN ==============================
2020-06-13 16:52:46 INFO http-nio-8080-exec-8 SDK_MSG_LOG ============================== SDK REQ MSG END ==============================
2020-06-13 16:52:46 INFO http-nio-8080-exec-8 ACP_SDK_LOG 验签处理开始
2020-06-13 16:52:46 INFO http-nio-8080-exec-8 cc.mrbird.febs.pay.unionpay.controller.UnionPayController 后台验证签名结果[失败].
2020-06-13 16:52:46 INFO http-nio-8080-exec-8 cc.mrbird.febs.pay.unionpay.controller.UnionPayController BackRcvResponse接收后台通知结束
异常原因
\*\*
因为Shiro会自动将原生request对象包装成ShiroRequest,上面的String notifyStr = readData(request)获取报文是从request.getReader()中获取,这里获取不到银联支付成功后返回的报文,所以导致验签失败! 。
查找资料,找到的解释如下:
今天发现一个很奇怪的问题,我们系统里用REST方式做前后端的访问,具体就是所有的请求都是POST,URL对应处理的action,HTTP body里的json是请求参数;后端程序里从Request.getReader拿到json参数,然后调用相应的action来处理。有一个登陆接口,在页面ajax和android里请求是正常的,但是在iphone里请求出错。调试了下,发现request.getReader拿不到数据。
通过tcpmon代理查看HTTP请求报文,对比下正确和错误的REST请求,感觉最大的问题可能是出现在Content-type上,进而发现application/x-www-form-urlencoded时,会出现这个问题,但是text/html,text/xml等格式都没有问题。这就能确定是Content-Type引起的了。
为什么Content-Type会引起这个问题呢?
我们知道request.getInputStream\getReader,都只能调用一次,调用过了,buffer里的数据就没有了。而request.getParameterXX方法也可能会解析buffer,如果这个调用过了,再调用getReader也会没有数据了。难道Content-Type不同的时候导致调用了这个?
调试下,追了追shiro和tomcat的代码,发现果然如此。
请求进来先进过shiro的filter,filter里试图拿到当前subject,先从cookie试着拿sessionId,不行就从URL中试着拿sessionId,还没有的话就从request.getParameter里获取sessionId,这里调用到了tomcat的request.getParameter实现:如果Content-Type是multipart/form-data或者application/x-www-form-urlencoded,则直接解析http body……问题就出现了。
简单的说就是,如果请求是application/x-www-form-urlencoded格式的话,shiro的filter在我们的servlet或action处理之前,就可能直接把request的body给读取并清空了。
通过这个实现我们也可以看到:除了这两个Content-Type之外的格式都不会有问题,登陆后sessionId放到cookie里了也不会有问题。
按理讲,我们用json应该手动设置Content-Type:application/json;因为application/x-www-form-urlencoded就是为了在body里使用类似QueryString的key-value格式。所以写代码的时候要注意了,如果某些HTTP库的实现里,默认POST是form格式的,如果你要自己处理HTTP body就需要手工的设置自己想要的Content-Type了。
知道怎么回事了,就很好处理了。在xcode里,将Content-Type改成text/html或application/json即可。
进一步的来看,shiro处理考虑到sessionId在cookie和url里,还考虑到一种情况:session藏在页面form的hidden里,通过form的post方式,把key-value作为http body来post到服务器端,shiro可能通过解析httpbody拿到sessionId,这个对session来说用处不大,因为我们一般都放到cookie或url的jsessionId里,但是对于某些需要自己处理http body的场景,shiro的filter显然破坏掉了后续对body的request.getReader读取。
版权声明:本文为CSDN博主「kimmking」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/kimmking/article/details/17505637
解决方法
上述文章已经很明确的告诉我们问题出现的原因了,那我们该如何处理呢?这里我们更改接收参数的方式。
代码示例
\*\*
这里采用@RequestParam来接收银联支付成功返回的参数是因为我们通过获取Content-Type发现,银联返回参数的Content-Type为:application/x-www-form-urlencoded; charset=UTF-8。
/**
* 后台回调
* @param request
* @param respParam 获取银联通知服务器发送的后台通知参数
* @return
*/
@RequestMapping(value = "/backRcvResponse", method = { RequestMethod.POST, RequestMethod.GET })
@ResponseBody
public String backRcvResponse(@RequestParam Map respParam) {
logger.info("BackRcvResponse接收后台通知开始");
String encoding = "UTF-8";
LogUtil.printRequestLog(respParam);
// 重要!验证签名前不要修改reqParam中的键值对的内容,否则会验签不过
if (!AcpService.validate(respParam, encoding)) {
logger.info("后台验证签名结果[失败].");
// 验签失败,需解决验签问题
} else {
logger.info("后台验证签名结果[成功].");
// 【注:为了安全验签成功才应该写商户的成功处理逻辑】交易成功,更新商户订单状态
String orderId = respParam.get("orderId"); // 获取后台通知的数据,其他字段也可用类似方式获取
String respCode = respParam.get("respCode");
// 判断respCode=00、A6后,对涉及资金类的交易,请再发起查询接口查询,确定交易成功后更新数据库。
logger.info("orderId>>>" + orderId + " respCode>>" + respCode);
}
logger.info("BackRcvResponse接收后台通知结束");
// 返回给银联服务器http 200 状态码
return "ok";
}
控制台日志
修改后我们来看下上述代码执行结果日志
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 cc.mrbird.febs.pay.unionpay.controller.UnionPayController BackRcvResponse接收后台通知开始
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG ============================== SDK REQ MSG BEGIN ==============================
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [accNo] = [6226********0048]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [accessType] = [0]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [bizType] = [000201]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [currencyCode] = [156]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [encoding] = [UTF-8]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [merId] = [777290058110048]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [orderId] = [1592039725990]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [payCardType] = [01]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [payType] = [0001]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [queryId] = [512006131715250536508]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [reqReserved] = [reqReserved]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [respCode] = [00]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [respMsg] = [成功[0000000]]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [settleAmt] = [6666]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [settleCurrencyCode] = [156]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [settleDate] = [0613]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [signMethod] = [01]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [signPubKeyCert] = [-----BEGIN CERTIFICATE-----
MIIEQzCCAyugAwIBAgIFEBJJZVgwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UEBhMC
Q04xMDAuBgNVBAoTJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhv
cml0eTEXMBUGA1UEAxMOQ0ZDQSBURVNUIE9DQTEwHhcNMTcxMTAxMDcyNDA4WhcN
MjAxMTAxMDcyNDA4WjB3MQswCQYDVQQGEwJjbjESMBAGA1UEChMJQ0ZDQSBPQ0Ex
MQ4wDAYDVQQLEwVDVVBSQTEUMBIGA1UECxMLRW50ZXJwcmlzZXMxLjAsBgNVBAMU
JTA0MUBaMjAxNy0xMS0xQDAwMDQwMDAwOlNJR05AMDAwMDAwMDEwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDIWO6AESrg+34HgbU9mSpgef0sl6avr1d
bD/IjjZYM63SoQi3CZHZUyoyzBKodRzowJrwXmd+hCmdcIfavdvfwi6x+ptJNp9d
EtpfEAnJk+4quriQFj1dNiv6uP8ARgn07UMhgdYB7D8aA1j77Yk1ROx7+LFeo7rZ
Ddde2U1opPxjIqOPqiPno78JMXpFn7LiGPXu75bwY2rYIGEEImnypgiYuW1vo9UO
G47NMWTnsIdy68FquPSw5FKp5foL825GNX3oJSZui8d2UDkMLBasf06Jz0JKz5AV
blaI+s24/iCfo8r+6WaCs8e6BDkaijJkR/bvRCQeQpbX3V8WoTLVAgMBAAGjgfQw
gfEwHwYDVR0jBBgwFoAUz3CdYeudfC6498sCQPcJnf4zdIAwSAYDVR0gBEEwPzA9
BghggRyG7yoBATAxMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmNmY2EuY29tLmNu
L3VzL3VzLTE0Lmh0bTA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vdWNybC5jZmNh
LmNvbS5jbi9SU0EvY3JsMjQ4NzIuY3JsMAsGA1UdDwQEAwID6DAdBgNVHQ4EFgQU
mQQLyuqYjES7qKO+zOkzEbvdFwgwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUF
BwMEMA0GCSqGSIb3DQEBBQUAA4IBAQAujhBuOcuxA+VzoUH84uoFt5aaBM3vGlpW
KVMz6BUsLbIpp1ho5h+LaMnxMs6jdXXDh/du8X5SKMaIddiLw7ujZy1LibKy2jYi
YYfs3tbZ0ffCKQtv78vCgC+IxUUurALY4w58fRLLdu8u8p9jyRFHsQEwSq+W5+bP
MTh2w7cDd9h+6KoCN6AMI1Ly7MxRIhCbNBL9bzaxF9B5GK86ARY7ixkuDCEl4XCF
JGxeoye9R46NqZ6AA/k97mJun//gmUjStmb9PUXA59fR5suAB5o/5lBySZ8UXkrI
pp/iLT8vIl1hNgLh0Ghs7DBSx99I+S3VuUzjHNxL6fGRhlix7Rb8
-----END CERTIFICATE-----]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [traceNo] = [053650]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [traceTime] = [0613171525]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [txnAmt] = [6666]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [txnSubType] = [01]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [txnTime] = [20200613171525]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [txnType] = [01]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [version] = [5.1.0]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG [signature] = [fMAWGyurRRKCk31AsG/J/0yKpk92ViOKmQ83O0OOgoN/iIy/cxUhGNgA6RqbScGZqLG5UVyQI/BUWCQgeuTX9MtA4sd4kmm3F9BgCR3s4kIcJqKM/0/x6ZdPeBU7nd8F/Aev07P3YA3wmzz0bbk/htrhFuH8rWQRArzCv8cLy78ZxKbetzI+BU8pKzghK7YPY7yTQmimYXkuIQpq38IJvWSzybvAwXzXre5m/DgXZLdFvLhuvsb0H2POwz+qN90FMpl7VlJAe+FhNi6yOIfKyvKZJUErG0uGQt5hSYoCzCcu3IUhYSwSzP8gfhKOrk3yWExefY90FKQOcyFTBfjZxg==]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 SDK_MSG_LOG ============================== SDK REQ MSG END ==============================
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 ACP_SDK_LOG 验签处理开始
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 ACP_SDK_LOG verify certificate chain succeed.
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 ACP_SDK_LOG 签名原文:[fMAWGyurRRKCk31AsG/J/0yKpk92ViOKmQ83O0OOgoN/iIy/cxUhGNgA6RqbScGZqLG5UVyQI/BUWCQgeuTX9MtA4sd4kmm3F9BgCR3s4kIcJqKM/0/x6ZdPeBU7nd8F/Aev07P3YA3wmzz0bbk/htrhFuH8rWQRArzCv8cLy78ZxKbetzI+BU8pKzghK7YPY7yTQmimYXkuIQpq38IJvWSzybvAwXzXre5m/DgXZLdFvLhuvsb0H2POwz+qN90FMpl7VlJAe+FhNi6yOIfKyvKZJUErG0uGQt5hSYoCzCcu3IUhYSwSzP8gfhKOrk3yWExefY90FKQOcyFTBfjZxg==]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 ACP_SDK_LOG 待验签返回报文串:[accNo=6226********0048&accessType=0&bizType=000201¤cyCode=156&encoding=UTF-8&merId=777290058110048&orderId=1592039725990&payCardType=01&payType=0001&queryId=512006131715250536508&reqReserved=reqReserved&respCode=00&respMsg=成功[0000000]&settleAmt=6666&settleCurrencyCode=156&settleDate=0613&signMethod=01&signPubKeyCert=-----BEGIN CERTIFICATE-----
MIIEQzCCAyugAwIBAgIFEBJJZVgwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UEBhMC
Q04xMDAuBgNVBAoTJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhv
cml0eTEXMBUGA1UEAxMOQ0ZDQSBURVNUIE9DQTEwHhcNMTcxMTAxMDcyNDA4WhcN
MjAxMTAxMDcyNDA4WjB3MQswCQYDVQQGEwJjbjESMBAGA1UEChMJQ0ZDQSBPQ0Ex
MQ4wDAYDVQQLEwVDVVBSQTEUMBIGA1UECxMLRW50ZXJwcmlzZXMxLjAsBgNVBAMU
JTA0MUBaMjAxNy0xMS0xQDAwMDQwMDAwOlNJR05AMDAwMDAwMDEwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDIWO6AESrg+34HgbU9mSpgef0sl6avr1d
bD/IjjZYM63SoQi3CZHZUyoyzBKodRzowJrwXmd+hCmdcIfavdvfwi6x+ptJNp9d
EtpfEAnJk+4quriQFj1dNiv6uP8ARgn07UMhgdYB7D8aA1j77Yk1ROx7+LFeo7rZ
Ddde2U1opPxjIqOPqiPno78JMXpFn7LiGPXu75bwY2rYIGEEImnypgiYuW1vo9UO
G47NMWTnsIdy68FquPSw5FKp5foL825GNX3oJSZui8d2UDkMLBasf06Jz0JKz5AV
blaI+s24/iCfo8r+6WaCs8e6BDkaijJkR/bvRCQeQpbX3V8WoTLVAgMBAAGjgfQw
gfEwHwYDVR0jBBgwFoAUz3CdYeudfC6498sCQPcJnf4zdIAwSAYDVR0gBEEwPzA9
BghggRyG7yoBATAxMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmNmY2EuY29tLmNu
L3VzL3VzLTE0Lmh0bTA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vdWNybC5jZmNh
LmNvbS5jbi9SU0EvY3JsMjQ4NzIuY3JsMAsGA1UdDwQEAwID6DAdBgNVHQ4EFgQU
mQQLyuqYjES7qKO+zOkzEbvdFwgwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUF
BwMEMA0GCSqGSIb3DQEBBQUAA4IBAQAujhBuOcuxA+VzoUH84uoFt5aaBM3vGlpW
KVMz6BUsLbIpp1ho5h+LaMnxMs6jdXXDh/du8X5SKMaIddiLw7ujZy1LibKy2jYi
YYfs3tbZ0ffCKQtv78vCgC+IxUUurALY4w58fRLLdu8u8p9jyRFHsQEwSq+W5+bP
MTh2w7cDd9h+6KoCN6AMI1Ly7MxRIhCbNBL9bzaxF9B5GK86ARY7ixkuDCEl4XCF
JGxeoye9R46NqZ6AA/k97mJun//gmUjStmb9PUXA59fR5suAB5o/5lBySZ8UXkrI
pp/iLT8vIl1hNgLh0Ghs7DBSx99I+S3VuUzjHNxL6fGRhlix7Rb8
-----END CERTIFICATE-----&traceNo=053650&traceTime=0613171525&txnAmt=6666&txnSubType=01&txnTime=20200613171525&txnType=01&version=5.1.0]
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 ACP_SDK_LOG 验证签名成功
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 cc.mrbird.febs.pay.unionpay.controller.UnionPayController 后台验证签名结果[成功].
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 cc.mrbird.febs.pay.unionpay.controller.UnionPayController orderId>>>1592039725990 respCode>>00
2020-06-13 17:15:51 INFO http-nio-8080-exec-8 cc.mrbird.febs.pay.unionpay.controller.UnionPayController BackRcvResponse接收后台通知结束
通过日志我们可以看出已经成功接收银联支付成功返回的报文,并且已经验证签名成功
\*\*
注意:代码示例中支付控制器包路径:cc.mrbird.febs.pay.unionpay.controller,这里只修改了后台回调(/backRcvResponse)和前台回调(/frontRcvResponse),其余如果使用到请参照修改。
参考资料
Shiro导致Request.getReader无法获取数据
Shiro导致request.getInputStream()无法获取数据
request.getReader()的作用