Grpc+HTTP/2
在上文我们通过Java来实现了Grpc的使用,但是有一点我们注意到,虽然使用的是HTTP/2协议进行通信的,但是为什么我们使用的是PLAINTEXT,HTTP/2的实现中间不是有一层TLS/1.2+吗,没证书还能给我整上加密了???
其实在HTTP/2的实现当中有一种h2c的协议升级方式,这种方式就是不带有加密TLS的协议。
H2C明文HTTP/2
普通浏览器实现HTTP/2需要先使用HTTP/1.x来请求服务器,在获取到服务器Upgrade:h2c(明文)返回后来转换协议
客户端 服务端 |
客户端的前言内容包含一个内容为 PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
的序列加上一个可以为空的 SETTINGS 帧,在收到 101(Switching Protocols) 响应 (代表 upgrade 成功) 后发送,或者作为 TLS 连接的第一个传输的应用数据。
如果在预先知道服务端支持 HTTP/2 的情况下启用 HTTP/2 连接,客户端连接前言在连接建立时发送。
我们的GRPC就是已知直接使用HTTP/2的情形,所以在建立连接的时候不需要HTTP/1.X来升级协议,在TCP握手之后直接使用Magic帧来确定协议
H2 TLS加密的HTTP/2
当然对于带有TLS的HTTP/2就不会通过HTTP/1.X来升级协议,他们会直接使用HTTPS来协商
HTTPS需要经过一个协议协商阶段来建立连接,在建立连接并交换HTTP消息之前,它们需要协商 SSL/TLS协议、加密的密码,以及其他的设置。这个过程比较灵活,可以引入新的HTTPS协议和密码,只要客户端和服务端都支持就行。在HTTPS握手的过程中,可以同时完成HTTP/2协商,这就不需要在建立连接时增加一次跳转。
上面提到的协商协议阶段就是使用ALPN来进行协商使用的协议
-
客户端发起 TLS 握手:
客户端在发起 TLS 握手请求时,会在 “ClientHello” 消息中包含一个 ALPN 扩展字段,列出它支持的协议列表,例如 http/1.1 和 h2(HTTP/2 的标识符)。
-
服务器响应:
服务器收到 “ClientHello” 消息后,在 “ServerHello” 消息中包含一个 ALPN 扩展字段,选择并响应一个客户端支持的协议,例如 h2,如果服务器支持 HTTP/2 并且客户端也支持。
-
建立 TLS 连接:
协商完成后,客户端和服务器按照协商的协议版本进行通信,并完成 TLS 握手过程。
这是正常HTTP/2的协议流程
使用TLS加密的Grpc
那我们该如何才能让我们的grpc使用TLS加密呢
前期准备
将配置的grpc.client.feignName.negotiationType=PLAINTEXT
删掉,配置这种情况表示走明文
需要让我们的服务支持HTTPS
我这边使用的是通义零码直接帮我做的
- 首先创建一个生成证书的配置,csm-csm是你服务在注册中心的名字,当然还有dns2,直接指向注册中心后面的域名也可以
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req
[dn]
C = CN
ST = Beijing
L = Beijing
O = Baiwang
OU = IT
CN = csm-csm
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = csm-csm
DNS.2 = localhost
DNS.3 = 127.0.0.1 - 通过配置文件生成证书
openssl req -new -x509 -newkey rsa:2048 -sha256 -keyout server.key -out server.crt -days 365 -nodes -config openssl.cnf -extensions v3_req
- 查看证书创建是否准确
验证生成的证书是否包含正确的SAN
openssl x509 -in server.crt -text -noout | grep -A5 "Subject Alternative Name"
返回内容
X509v3 Subject Alternative Name:
DNS:csm-csm, DNS:localhost, DNS:127.0.0.1
X509v3 Subject Key Identifier:
CB:6D:C5:23:E1:99:CC:B3:74:DC:B3:8F:5C:1D:0A:05:2A:EE:18:CB
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
检查证书的完整信息 - 配置服务端的Grpc
grpc:
server:
port: 9099
security:
enabled: true
certificate-chain: classpath:certificates/server.crt
private-key: classpath:certificates/server.key
配置客户端的Grpc
grpc: |
这样我们在使用grpc调用的时候就是带证书的访问了
未使用TLS
使用TLS
这两张图片完整的表示了HTTP/2的ALPN
因为被证书加密了,我们也无法看到传输的具体内容,甚至请求URL也看不到了(HTTPS的是可以看到的,我并不确定这两者的区别)