经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
支付宝 v3 验签如何实现
来源:cnblogs  作者:盐焗代码虾  时间:2023/12/29 9:18:35  对本文有异议

上次给大家介绍了 支付宝 v3 自签名如何实现 ,这次顺便再把验签也写一下。

 

为什么要验签

说起为什么要验签,如果要详细一点解释的话,可以写很多很多......

我们就简单一点来解释:验签可以证明接收到的信息是支付宝给我的,不是被人中途拦截篡改数据之后再发给我的。

 

支付宝的通知分为 「同步通知 」和 「异步通知 」:

  • 「同步通知 」就是我们请求支付宝之后,支付宝返回的数据。
  • 「异步通知 」是到达某些条件之后,支付宝主动发的;更详细内容可以参考之前我写的 [手把手|支付宝异步通知如何使用]。

 

对于这两种通知我们都需要进行验签处理,才能保证数据的准确性!(?? 很重要!!)

 

其实支付宝给的 SDK 里面也封装了验签的方法,并且对同步通知都经过了验签的处理,同步验签不过的话,接口会直接抛出异常 [sign check fail: check Sign and Data Fail]。

另外 v3 SDK 里面提供的验签方法名跟 v2 版本是一样的,大家不想麻烦的话可以直接查看 [SDK如何实现验签]。

(大家凑合看,v3 版本好像还没有完整的验签示例代码,也可能是我没找到 ??

 

虽然给了这么多简单的方法,但是我就是要自!己!写!一!遍!╭(╯^╰)╮

 

如何验签

验签的流程比加签要简单一点,下面用支付宝同步通知的数据做验签例子

 

步骤一、接收支付宝返回的信息

首先就是要接收到支付宝返回的信息,因为是同步验签的数据,直接拿之前自签名的代码改一下

验签我们所需要的数据有:

  • aliapy-signature:支付宝生成的签名内容。
  • alipay-timestamp:支付宝应答时间戳。
  • alipay-nonce:支付宝应答随机串。
  • httpResponseBody:响应报文内容,自签名的 resData 数据。

接收代码

  1. //获取响应的请求头head
  2. Header[] responseHeader = response.getAllHeaders();
  3. //待验签数据head:aliapy-signature、alipay-timestamp、alipay-nonce
  4. String alipaysignature = response.getFirstHeader("alipay-signature").getValue();
  5. String alipaytimestamp = response.getFirstHeader("alipay-timestamp").getValue();
  6. String alipaynonce = response.getFirstHeader("alipay-nonce").getValue();

 

获取到的响应值

  1. httpResponseBody:{"out_trade_no":"20181128763521373251698","qr_code":"https://qr.alipay.com/bax04870evi3w2dlaeai2502"}
  2. aliapy-signatureM/6yx2OajiQD0mM9Tk9ShsduFERtmj+xI0BN8QiZk8BMUCvMQCne1n/VIbMZ738k4No8nsE1DC0saPe2NqtmgxC3B+TmWgrhJ+4JOVEc7K4/LcIDWN2PaPCw5g5+oUQRIGCbo0+f9yqSew4NwETV2RiVIw91q+kJ4OeIpauSnGQAuwOxqciDM52k7gUhij8G+evhK7xn6TNhiQgRk0RjkyhEEp/00lYb5xI2d9Oj5KgsDC9KTRo9SO0SJaH0SbfNHU40XUkkomuj6jiOEeccfB6Fofzq5jfL3u24Ev9SxTDf2kYZzffShLrYhlrI8947VqC3h8/F6O8y4K/PQl3LCw==
  3. alipay-timestamp1703576825544
  4. alipay-nonce73b3422127c9996ad405e77091eef6f4

 

 

包含之前自签名的完整代码(仅供参考)

  1. public class V3HttpPostTest {
  2. public static void main(String args[]) throws Exception {
  3. // 发送请求的url
  4. String url = "https://openapi.alipay.com/v3/alipay/trade/precreate";
  5. // 发送请求的内容
  6. String content = "{\"out_trade_no\":\"20181128763521373251698\",\"total_amount\":\"1\",\"subject\":\"123\",\"body\":\"body\"}";
  7. String chearset = "utf-8";
  8. // 创建请求对象:post或者get
  9. HttpPost httpPost = new HttpPost(url);
  10. // httpClient实例化
  11. CloseableHttpClient httpClient = HttpClients.createDefault();
  12. // 设置类型
  13. // "application/x-www-form-urlencoded","application/json"、multipart/form-data、text/xml
  14. httpPost.setHeader("Content-Type", "application/json");
  15. // 调用方的requestId,用于定位一次请求,需要每次请求保持唯一。
  16. httpPost.setHeader("alipay-request-id", "32432432432423421");
  17. httpPost.setHeader("authorization",
  18. "ALIPAY-SHA256withRSA app_id=2021111111111122,timestamp=1702452177941,nonce=3246658768654544,sign=WDF6pS2qK/kEZnsJDMrhNmd/z82ClZ+VMohYxIUs3MZ2j0m+4reQtSBGa6mZyA5ffbIPPvZTRO+1DLEuuCvZRMQGK3okYSA/ASP7GEqfCDeKmkqzKV2kWrmftNfO+EiIiCnsiyJG4SQ9G7s0OtmCT6wVkphW9wgk7mfUoF5a+Wo3kzvEur3U+7ZfSgLa4HXQG2xE+z7BjmHG8j1qVoVa/3TR1lVBAqOwkodZ9cSPKceK2RxaPkk8gsFbofbuARl5xBqDwkS2caTQu27+DLXT/QJOHRHRw5VtH9v8B7nT+nrijFjktm6hD7aIHuPon6TtEgnbtWltRizEZldh+Fo1Eg==");
  19. // 支付宝根证书序列号,使用证书模式时,需要传递该值
  20. // httpPost.setHeader("alipay-root-cert-sn", "");
  21. // 组织数据
  22. StringEntity se = null;
  23. try {
  24. se = new StringEntity(content);
  25. // 设置编码格式
  26. se.setContentEncoding(chearset);
  27. // 设置数据类型
  28. se.setContentType("application/json");
  29. // post请求,将请求体填充进httpPost
  30. httpPost.setEntity(se);
  31. // 通过执行httpPost获取实例
  32. HttpResponse response = httpClient.execute(httpPost);
  33. HttpEntity entity = response.getEntity();
  34. String resData = EntityUtils.toString(entity);
  35. System.out.println("httpResponseBody:" + resData);
  36. //获取响应的请求头head
  37. Header[] responseHeader = response.getAllHeaders();
  38. //待验签数据head:aliapy-signature、alipay-timestamp、alipay-nonce
  39. String alipaysignature = response.getFirstHeader("alipay-signature").getValue();
  40. String alipaytimestamp = response.getFirstHeader("alipay-timestamp").getValue();
  41. String alipaynonce = response.getFirstHeader("alipay-nonce").getValue();
  42. System.out.println("aliapy-signature:" + alipaysignature);
  43. System.out.println("alipay-timestamp:" + alipaytimestamp);
  44. System.out.println("alipay-nonce:" + alipaynonce);
  45. // 关闭httpClient资源
  46. httpClient.close();
  47. } catch (Exception e) {
  48. e.printStackTrace();
  49. }
  50. }
  51. }

 

 

步骤二、拼接待验签内容

接收到响应数据之后,我们需要按照

  1. ${alipay-timestamp}\n
  2. ${alipay-nonce}\n
  3. ${httpResponseBody}\n

规则,将数据组装起来。

 

content 组装示例

【注意:\n 不要丢!】

  1. String content = alipaytimestamp + "\n" + alipaynonce + "\n" + httpResponseBody + "\n";

返回值

  1. 1703576825544
  2. 73b3422127c9996ad405e77091eef6f4
  3. {"out_trade_no":"20181128763521373251698","qr_code":"https://qr.alipay.com/bax08770vkyzjc0is6ep25ed"}

 

步骤三、进行签名比对

组装完待验签内容之后,我们就可以将数据进行验签了,其中用到的参数有:

  • content:上一步获取到的内容。
  • alipaysignature:第一步获取到的 alipaysignature。
  • publicKey:为支付宝公钥,在支付宝平台上传应用公钥后获取,参考 [如何获取支付宝公钥]。
  • charset:编码格式,代码中用的是 UTF-8。

验签代码

  1. private static boolean doVerify(String content, String alipaysignature, String publicKey, String charset) throws Exception {
  2. try {
  3. byte[] encodedKey = publicKey.getBytes();
  4. encodedKey = Base64.getDecoder().decode(encodedKey);
  5. PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(encodedKey));
  6. java.security.Signature signature = java.security.Signature.getInstance("SHA256withRSA");
  7. signature.initVerify(pubKey);
  8. signature.update(content.getBytes(charset));
  9. boolean signVerified = signature.verify(Base64.getDecoder().decode(alipaysignature.getBytes()))
  10. System.out.println("signVerified:" + signVerified);
  11. return falg;
  12. } catch (Exception e) {
  13. String errorMessage = "验签失败,请检查公钥格式是否正确。content=" + content + " publicKey=" + publicKey + " reason="
  14. + e.getMessage();
  15. throw new Exception(errorMessage);
  16. }
  17. }

返回值

  1. signVerified:true

只有返回 true 才能说明验签是通过的。

 

写在最后

v3 验签写完之后,可以看到其实跟 v2 的验签方法没有什么区别,最大的不同点在于待验签内容,大家都可以试试看,还是挺简单的。

 

原文链接:https://www.cnblogs.com/yjdmx/p/17933026.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号