00:00
这节课呢,我们来讲一下签名的底层原理和执行的流程,那我们知道,当我们点击应用程序当中的确认支付展示二维码的过程当中呢,实际上呢,是调用了我们微信支付的统一下单接口,那么调用统一下单接口的流程呢,实际上就是这个流程。那你会发现这个流程当中,它有一个完整的请求和响应。那这个完整的请求和响应呢?之前我们在。API证书和的使用这个当中呢,也见到过它的细节,那它的细节是这样的。首先呢,商户发送请求的时候要计算一个签名,那我们接收到微信支付给我们的响应的时候呢,我们要验证签名,那这堂课呢,我们来详细的去分析一下签名的计算的过程,以及携带签名发送请求的过程。
01:01
首先呢,我们在应用程序当中啊,之前我们已经打印了几个日志,我们呢先来看一下,那么我们在微信配这个类当中,也就是这个类哈,设置了一个SLFJ啊这样的一个注解。那么使用这个注解之后呢,我们就可以在应用程序当中用log的形式呢去打印日志了,那么我们先打印一个log.info日志。这个地方如果我们应用程序启动的话,它就会被执行,因为这是有一个叫做at b这样的一个注解,所以呢,我们整个应用程序启动的时候呢,这个对象就会被初始化出来,这是我们的。签名验证器,接下来呢?签名验证器初始化出来之后呢?到我们的get微信client中,那这个法clientt client。
02:17
应用程序的接口的调用,我们前面是在微信pay controller这个地方呢,写了一个native pay这样的一个方法,在这个方法当中呢,我们也打印了一个日志,叫做发起支付请求。接下来呢,我们进入到这个native pay这个业务层的方法当中,这个位置呢,我们也打印了一个日志,叫做生成订单,好,然后再往下。整个的这个过程呢,是我们组装http post请求对象,并且组装请求参数的一个过程,最后呢,我们把请求参数呢封装成了Jason字符串,然后呢,我们又把这个Jason字符串呢进行了打印输出,所以这个地方呢,我们也有一个日志的输出,再接下来呢,我们就对我们的http post对象呢进行了设置,那么设置的是啊,要求我们的请求体的内容呢,必须是Jason格式,这句话的意思呢,就是我们接收的响应的内容呢,也要求必须是Jason格式。好那么重点来了,重点在哪呢?在这句话就是微信配client.execute,那么在这句话执行的过程当中呢,我们的应用程序完成了两个非常重要的操作。
03:34
一个呢就是私要签名的过程,另一个呢就是公钥验签的过程,那这节课呢,我们先对私要签名的过程呢做一个梳理。所以呢,我们先启动我们的应用程序,那么在启动之前呢,我先来清空一下控制台。并且呢,用debug的形式启动我们的应用程序。
04:06
好,我们的应用程序呢,已经启动成功了,我们先寻找一下我们的client对象以及。Very对象他们初始化的痕迹哈,所以呢,我们来在控制台的日志当中查找这个日志,好这个位置呢,就是获取签名验证的一个过程,然后再往后。查找一下获t client获名后紧取http client了,好,所以呢,我们应用程序启动的时候,这两个对象呢,就已经被初始化出来了,那么接下来呢,我们来看一下我们点击。确认支付按钮的过程当中,我们的应用程序呢?都做了哪些事情?我们点击一下这个确认支付按钮。
05:08
好,那么在我们的应用程序当中,我们来看一下确认支付这个步骤他都做了什么,那根据我们的线索哈,我们打印的日志这样的一个线索。首先呢,它是在微信配controller这个里面呢,打了我们输出的一个日志,就发起支付请求,接下来呢,在微信配service ML这个类里面呢,打印了我们输出的接下来的日志,生成订单调用,统一下单API以及请求参数啊的一个组装的一个结果也输出出来了,然后接下来呢,当我们将这个请求发送出去之后。这个位置哈,就是第88行这个位置啊,当我们把请求发送出去之后呢,那么我们的应用程序呢,就会返回这样的一个结果啊,在后面这个位置就会输出了我们的这个结果,所以这个其实看起来就是一个很简单的应用程序的执行流程,那这里面的细节隐含在哪了呢?隐含在了它的debug日志当中,所以默认情况下你会发现呢,我们info日志输出的内容非常少啊,再加上我们自己主动输出的内容呢,我们其实是得不到什么非常有效的信息的,所以呢,大家如果想研究我们整个应用程序执行流程的底层的话呢,那么建议大家直接打开我们的debug日志级别,所以呢,我们打开我们的应用程序的配置文件。
06:36
那么在这个地方呢,我们打开一个日志级别。用level啊,然后呢,Root跟日志记录器的级别呢,我们给他设置成debug。好,接下来呢,我们重新呢,以debug的形式启动我们的应用程序。
07:05
好,这一次呢,你会发现。这个日志呢,好像显得就尤为多了,是不是啊好,那么系统的启动日志呢,我们这堂课先不看哈,我们这堂课呢,主要来看我们的。Native下单的这个日志,所以我们先等一会儿,等他把日志都打印完啊,好,现在呢,这个日志已经打印完成了,我们先把这个控制台清空,然后接下来呢,我们再重新点击确认支付按钮,这一次呢,我们就要啊去认真的分析我们的这个确认支付,它究竟底层都做了什么,好,我们点击一下。这次呢,我们来看一下整个的控制台上的日志呢,就非常多了,那为了便于查找,所以呢,我们先打开我们的CR了,我们先搜索一下发起支付请求这块。那我们在日志啊,这个面板当中呢,查找一下我们的发起支付请求,那你会发现前面其实都是一样的,发起支付请求,生成订单,调用统一下单API,然后组装请求参数,一直到什么位置就变了呢?在组装完请求参数响应发回来之前,也就是说之前我们组装完请求参数,直接就得到了这个成功的返回结果,对不对。
08:23
那么在响应发回来之前呢,中间穿插了很多其他的日志啊好,我们来看一下中间的这个日志,那首先呢,我们会发现这个里面。有一个叫做authorization message的这样的一个日志记录,而这个日志记录来源于哪呢?来源于WeChat pay To Credentials,这个类是什么呢?这个类就是我们之前引入的微信支付的SDK里面给我们提供的这个源代码,我们来看一下。我们之前曾经引入一个微信支付的类。
09:03
那这个呢,就是我们之前引入的微信支付的SDK,我们说我们通过这个SDK是不是创建了微信里面我们的http client这个对象和。Very fair这两个对象啊,好,那这两个对象之前我们说过,我们通过一系列的参数的组装啊,必要条件的组装,让这两个类能够有什么功能呢?就是能够有自动的验签和自动的签名的功能,对不对?好,那现在呢,实际上这两个类其实呢,他们就开始工作了,那么他具体是如何工作的呢?我们先打开一下我们的。文档,文档当中呢,有一个叫做接口规则。开发指南签名生成,我们打开这个文档啊,在这个文档当中呢。他描述了我们向微信端发送请求的时候,也就是这个流程当中,商户这面的应用程序当中计算签名的一个具体的流程,那现在有了SDK呢,他就把这个签名的过程呢给咱们封装起来了,但是呢,如果你想要了解它签名实现的底层原理的话,我们呢要分析一下它的源代码,并且呢,要查看一下它签名具体的生成的过程是什么样的,那我们就来读一下这篇文档。
10:24
首先呢,商户可以按照下述步骤生成请求的签名。那么微信支付APIV3要求商户对请求进行签名,好,微信支付会在收到请求后进行签名验证,如果签名验证不通过,微信支付APIV3将会拒绝处理请求,并返回401,所以大家自己在调用这个远程接口的时候,如果你发现是401这个的话,那么很有可能是前面我们签名的过程出现问题了啊,当然现在咱们用的是SDK,那SDK把这个过程都封装起来了,那我们出现这个签名问题的几率呢就比较少了,那什么时候会出现呢?比如说你提供的这些基本参数不对的时候。
11:13
比如说我们提供了。错误的商户API证书的序列号,或者是我们提供了错误的ad等等啊,我们提供的这些基本参数有问题的时候,那么很有可能呢,就会出现这个401的这样的一个错误了,所以呢,这个是啊微信端验签的一个流程,那我们继续往下看,那商户呢,需要拥有一个微信支付商户号,那也就是说我们需要准备这个商户号,并通过超级管理员账号登录商户平台获取商户API证书。API证书的压缩包中包含了签名必须的私钥和商户证书,那么这个位置呢,就是我们的商户证书的序列号,它可以帮助我们去找到对应的商户证书。
12:03
那这个位置呢,就是刚才提到的商户的私钥文件,那这些呢都是必要的条件,好接下来呢,我们来看一下构造签名串的具体的过程,我们希望商户的技术开发人员按照当前文档的约定的规则构造签名串,微信支付呢会使用同样的方式构造签名串。这个就有点儿类似于咱们前面在支付安全当中讲的啊,我们呢先做一个摘要对吧,然后呢,发送给对方之后呢,对方呢,会用同样的方式呢,也计算出一个摘要,最后呢,会将两个摘要呢进行比较啊,所以这个其实就是签名和验签过程当中的一个非常重要的步骤,那如果商户构造签名串的方式是错误的,将导致签名的验证不通过,所以那如果我们没有通过正确的方式构造签名串的话呢,那么微信支付那面他所构造出来的签名串和我们就不会一样了,就会有出入了,那么对比的时候,比对的时候呢,就会造成签名不通过的这样的一个问题。
13:08
那么接下来呢,我们就说一下签名串的具体的格式,签名串呢,一共有五行,每一行为一个参数,每一行的行尾呢,都要用一个换行符来结束。那么也就是说下面的这个,其实呢,就是我们构造的完整的一个签名串了,你会发现呢,这里面有HTTP请求方法,URL,请求时间戳,请求随机串和请求报文主体,所以呢,这是整个的第一个步骤啊,构造签名串的过程。我们把它。做一个笔记相当于哈,在屏幕上做一个笔记,把它留住啊,一会我们要核对它,那么现在呢,其实我们就可以核对一下它,我们打开刚才我们的日志。在debug这个里面,那我们可以发现在这个位置其实就是一个很典型的签名串,哈哈,我们来比对一下。
14:07
那么你发现呢,这就是T请方法啊,这就是我们前的这个URL,我们用的是native支付API,而这个就是我们的native支付API的一个路径啊,然后这块就是我们的请求时间。这很显然是一个随机字符串,那么整个这个位置最后呢,是我们的请求报文的主题,所以你会发现在日志当中明显他在做什么呀,他在做构造签名串的这么一个工作,对不对?那么接下来我们再回到文档当中。好,那下面这块就是它具体的构造签名砖的过程了。我们就。再往下看。第二个步骤呢,是计算签名值。计算签名值的过程是这样的,绝大多数编程语言提供的签名函数支持对签名数据进行签名,强烈建议商户调用该类函数,也就是说实际上在我们Java语言当中啊,是有这种函数啊,对刚才的这个签名串可以构造成签名值的。
15:19
使用商户私钥对代签名串进行HA256WITH RA签名,并对签名结果进行BASE64编码,得到签名值,那么。我们之前学过是一个很典型的摘要算法,也就是说他要先把这个签名进行摘要计算啊,然后接下来rsa是我们学过的一个典型的非对称加密,那很显然它要用私钥对我们的签名串所生成的这个摘要进行非对称加密,这是不是和我们之前讲过的签名的流程不谋而合呀?
16:03
并对签名的结果进行四编码,那么它这个地方呢,又多了一个步骤啊,也就是说摘法之后呢,再经过私钥加密,非对称加密,然后呢,还要进行BASE64编码。最后呢,我们得到了一个签名的值,所以呢,这是整个过程的第二个步骤,叫做计算签名值。好,接下来呢,我们再往下看。下面呢,就是在签名的最后啊,因为现在通过这个步骤,我们的签名其实已经生成了,但是呢,我们要把生成的这个签名呢,随着我们整个的请求呢,发送给微信的服务器端,那么怎么去发送呢?我们要设置HTTP头。啊,微信支付商户APIV3要求通过HTTP的authorization头来传递签名,也就是说在HTP当中传递这个签名,而这个头的名字呢,必须是authorization authorization呢由认证类型和签名信息两个部分组成,那如果我们通过这个呃,字符串的形式去表达的话呢,它就是这样的一个形式叫authorization冒号,然后认证类型字符串,然后空格,然后是签名信息,那么具体的认证类型呢,应该是WeChat pay two sha256r sa2048,也就是说实际上这个就是在我们的请求当中,你要告诉微信我们用了什么样的一个摘要算法和加密算法对我们的签名串进行签名。
17:57
然后接下来呢,签名信息这一部分,也就是说认证类型这一部分呢,我们要写这个签名信息这一部分写什么呢?首先啊,第一个部分的内容呢,写商户号啊,商户号也就是我们在配置文件当中。
18:14
配置的这个MCID,那第二个步骤呢,我们要写商户的API证书啊,也就是我们在配置文件当中配置的这个API证书序列号,第三个呢,我们要写请求随机串,第四个呢是时间戳,第五呢是签名值,那么它构造出来之后长什么样子呢?长这个样子。我们要把这个字符串呢,加到我们的T当中,所以呢,这块就是咱们前面约定好的authorization two sa256r sa2048,然后紧接着呢,就是这个。MD啊,接下来呢,就是就是这个们的顺序啊,是没有的顺序要啊,然后sial number我们来找一下在哪这呢是吧?嗯,然后接下来呢,是啊,请求随机串在这个位置,再接下来呢,是time step。
19:15
在。这个位置啊,最后呢是signature,也就是这个步骤啊,这个步骤我们计算出来的这个签名的值啊,通过chara和贝斯64最后得到的签名的值就是它。好,那么所以呢,这是我们的第三个步骤,叫做设置HTTP头。好设置HTP的具体的方式呢,是这样的。啊,要有这样的一个流程。然后接下来我们再往下看。现在呢,我们的这个HTP头呢,设置好了,设置好了之后呢,我们可以组装一个。
20:02
包含签名的HTP请求了,那么整个HTP请求发送呢?啊,如果用命令行发送这个请求的话,我们是这样写的啊,如果用代码来发送我们的HTP请求的话,那么就是这个了。那么就是这句话了啊好,那所以说呢,如果用命令行发送的话呢,我们这样去发送啊,首先呢,我们要知道这个请求的地址是什么,当然他举的例子呢,是另外的一个接口地址啊好,然后接下来呢。杠H的意思呢,就是在这个请求当中啊,去携带我们的签名信息,好,那把前面咱们计算出来的。HTP这一部分的内容就用杠H这个参数携带过去了,那这样的话,我们整个的这个远程请求呢,就携带着签名发送给微信的服务器了,好,那么我们再来看一下我们的这个代码。
21:05
我们的这个代码当中呢,前面第一个步骤,我们呢,知道它是在构造签名串,那我们再来看第二个步骤。很显然这个位置。这个位置哈,叫authorization token,诶他得到的信息是什么信息呀,是不是。签名信息呀,签名信息,因为签名信息这块是不是有什么呀,有我们的商户ID,有序列号,有随机串,有时间戳,有签名值,而这面是不是商户ID序列号。时间戳随机数签名值对吧?啊,一直到这啊整个都是签名值,所以这个地方它在做什么,在。设置HTP头当中的签名信息啊,他在做这件事情,好,接下来呢,再往后。
22:09
我们搜一下吧,搜一下这个啊,因为他紧接着要做的事情呢,就是啊把这个。认证类型要加在签名信息的前面,对吧?啊,所以我们这样搜一下叫WeChat pay。好,那么大家看就在这。所以这个位置呢,就构建了一个完整的。HTP头,那你会发现和我们啊,这面约定的这个内容是不是一模一样啊,它也包含什么,包含authorization冒号,然后包含我们的认证类型啊。认证类型,认证类型包含我们的后面刚才看到的这个签名信息这一部分,一直到这个地方都是我们的签名信息,好那么到这为止呢,他就组装完了一个完整的HTP请求啊好组装完这个HTP请求之后呢,它就会把这个请求呢发送出去,那么发送的地址呢,就是我们的微信服务器了,好然后发送出去之后。
23:21
这面呢,还有一些细节,就是具体发送的流程啊,比如说这个是我们的客户端的信息啊,用的是Java1.8,那这是我本地的这个客户端安装的版本哈,它都能够给你监测出来,并且打印出来,然后呢,这个是我发送的具体的请求的地址,那这个请求的地址呢,就是我们的的实际的一个请求头啊,所以在这个地方呢,就又打了我们发送的整个流程当中的HTP的实际的请求,那和刚才我们组完成之后输出出来的这个肯定是一模一样的了,好,那么他这边记录了整个HTTP请求的一个发送的流程之后呢,紧接着到最后我们就会得到响应了。
24:14
啊,那我们得到的响应呢,是成功的响应。那在这个响应当中呢,我们再往后就是要继续验签的流程了,所以呢,你会发现在我们的日志当中,它清晰的把整个签名生成的流程是不是打印出来了,而签名生成的流程是不是正是按照我们的签名生成指南当中的这个步骤一步一步来做的呀?
我来说两句