Skip to content
文档
Credentials

Credentials

gRPC 建立在HTTP/2协议之上,对 TLS 提供了很好的支持。 当不需要证书认证时,gRPCity默认是跳过了对服务器证书的验证。 没有启用证书的 gRPC 服务和客户端进行的是明文通信,信息面临被任何第三方监听的风险。 为了保证 gRPC 通信不被第三方监听、篡改或伪造,可以对服务器启动 TLS 加密特性。

注意: 启动 TSL 后会对 CPU 的资源消耗是会增加的,如果是你的业务场景是高 QPS 的,不建议启动 TSL。

生成证书

创建自己的SSL证书。首先,我们需要为服务器和客户端创建自己的SSL证书。可以使用 OpenSSL 命令来生成自己的证书。

我们使用bash脚本来完成证书的生成,先设置全局变量,如密码和证书有效期。

# 创建 certs 保存目录
mkdir -p certs && cd certs
 
# 设置密码变量
password="grpcity"
 
# 证书有效期变量
days=365

生成 ca 证书

# 设置CA相关变量
ca_key="ca.key"
ca_crt="ca.crt"
ca_subject="/C=CN/ST=GD/L=Guangzhou/O=gRPCity/OU=gRPCity/CN=ca"
 
# 生成CA密钥和证书
openssl genrsa -passout pass:$password -des3 -out $ca_key 4096
openssl req -passin pass:$password -new -x509 -days $days -key $ca_key -out $ca_crt -subj  "$ca_subject"

生成 server 证书

# 设置服务器相关变量
server_key="server.key"
server_csr="server.csr"
server_crt="server.crt"
server_subject="/C=CN/ST=GD/L=Guangzhou/O=gRPCity/OU=Server/CN=localhost"
 
# 生成服务器密钥和证书
openssl genrsa -passout pass:$password -des3 -out $server_key 4096
openssl req -passin pass:$password -new -key $server_key -out $server_csr -subj  "$server_subject"
openssl x509 -req -passin pass:$password -days $days -in $server_csr -CA $ca_crt -CAkey $ca_key -set_serial 01 -out $server_crt
openssl rsa -passin pass:$password -in $server_key -out $server_key

生成 client 证书

# 设置客户端相关变量
client_key="client.key"
client_csr="client.csr"
client_crt="client.crt"
client_subject="/C=CN/ST=GD/L=Guangzhou/O=gRPCity/OU=Client/CN=localhost"
 
# 生成客户端密钥和证书
openssl genrsa -passout pass:$password -des3 -out $client_key 4096
openssl req -passin pass:$password -new -key $client_key -out $client_csr -subj  "$client_subject"
openssl x509 -passin pass:$password -req -days $days -in $client_csr -CA $ca_crt -CAkey $ca_key -set_serial 01 -out $client_crt
openssl rsa -passin pass:$password -in $client_key -out $client_key

最终我们获得如下的证书目录:

Terminal
.
└── certs
    ├── ca.crt
    ├── ca.key
    ├── client.crt
    ├── client.csr
    ├── client.key
    ├── server.crt
    ├── server.csr
    └── server.key

服务端配置

我们开始进行服务端证书配置的工作。使用loader.makeServerCredentials() 进行证书加载。

loader.makeServerCredentials(rootCerts, keyCertPairs, checkClientCertificate) 的参数说明如下:

  • rootCerts: 根证书,一般为 ca 证书;
  • keyCertPairs: 证书对,key和crt,必须数组;
  • checkClientCertificate: 是否检查客户端证书;

示例:

import fs from 'node:fs'
import loader from './loader.js'
 
const credentials = loader.makeServerCredentials(
    fs.readFileSync(path.resolve(__dirname, 'certs/ca.crt')), [{
        private_key: fs.readFileSync(path.resolve(__dirname, 'certs/server.key')),
        cert_chain: fs.readFileSync(path.resolve(__dirname, 'certs/server.crt'))
    }], true)

两种方式把证书加载到服务端中:

1.在初始化的时候

const server = await loader.initServer({ credentials })

2.在监听启动的时候

await server.listen(addr, credentials)

优先级:listen() 优先于 initServer()

⚠️

使用了证书后,不能使用 ip 来启动服务,可以使用localhost或域名。

客户端配置

我们开始进行服务端证书配置的工作。使用loader.makeCredentials() 进行证书加载。

import fs from 'node:fs'
import path from 'node:path'
 
const credentials = loader.makeClientCredentials(
    fs.readFileSync(path.resolve(__dirname, 'certs/ca.crt')),
    fs.readFileSync(path.resolve(__dirname, 'certs/client.key')),
    fs.readFileSync(path.resolve(__dirname, 'certs/client.crt'))
)

获得credentials后,有两种加载方式,如下所示:

  1. initClients() 时加载
const clients = await loader.initClients({
    services: {
        'test.helloworld.Greeter': addr,
        'test.helloworld.Hellor': addr
    },
    credentials
})
  1. get()getReal() 时加载
const greeterClient = clients.get('test.helloworld.Greeter', { credentials })
const result = await greeterClient.sayHello({ name: 'greeter' })
⚠️
  • initClients() 适合所有服务端都是使用同一个证书的场景
  • get() 适合不同服务端的不同证书的场景