国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

gRPC — SSL/TLS單向認(rèn)證、雙向認(rèn)證、Token認(rèn)證

這篇具有很好參考價(jià)值的文章主要介紹了gRPC — SSL/TLS單向認(rèn)證、雙向認(rèn)證、Token認(rèn)證。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

一、什么是SSL/TLS?

傳輸層安全性協(xié)議(Transport Layer Security,縮寫作 TLS ),其前身安全套接層(Secure Sockets Layer,縮寫作 SSL )是一種安全協(xié)議,目的是為互聯(lián)網(wǎng)通信提供安全及數(shù)據(jù)完整性保障。根據(jù)傳輸層安全協(xié)議的規(guī)范,客戶端與服務(wù)端的連接安全應(yīng)該具備連接是私密的或連接是可靠的一種以上的特性。
SSL/TLS 協(xié)議通過 X.509 證書的數(shù)字文檔將網(wǎng)站的公司實(shí)體信息綁定到加密密鑰,每一個(gè)密鑰對都有一個(gè)私有密鑰和一個(gè)公有密鑰。私有密鑰是獨(dú)有的,一般位于服務(wù)器上,用于解密由公有密鑰加密過的信息,公有密鑰是公開的,與服務(wù)器進(jìn)行交互的每個(gè)人都可以持有公有密鑰,用公有密鑰加密的信息只能由私有密鑰來解密。

SSL/TLS 協(xié)議提供以下的服務(wù):

認(rèn)證用戶與服務(wù)器,確保數(shù)據(jù)發(fā)送到正確的客戶端和服務(wù)器;

加密數(shù)據(jù)以防止數(shù)據(jù)在傳輸過程中被竊取;

維護(hù)數(shù)據(jù)的完整性,確保數(shù)據(jù)在傳輸過程中不被改變。

二、什么是 SAN?

SAN(Subject Alternative Name) 是 SSL 標(biāo)準(zhǔn) x509 中定義的一個(gè)擴(kuò)展,使用了 SAN 字段的 SSL 證書,可以擴(kuò)展此證書支持的域名,使得一個(gè)證書可以支持多個(gè)不同域名的解析。

三、gRPC 的 SSL/TLS 加密認(rèn)證的實(shí)現(xiàn)

該部分內(nèi)容基于上一個(gè)博客gRPC-入門示例的代碼,進(jìn)行進(jìn)一步實(shí)現(xiàn)!如圖所示,回顧上一個(gè)博客中g(shù)RPC入門示例代碼,可以看出客戶端和服務(wù)端之間無任務(wù)加密認(rèn)證的過程,具有不安全性。因此,接下來的內(nèi)容將在原有代碼基礎(chǔ)上,分別實(shí)現(xiàn)SSL/TLS的單向認(rèn)證、雙向認(rèn)證、Token認(rèn)證的過程,增加安全性。
使客戶端支持 tls 單向身份驗(yàn)證,ssl,網(wǎng)絡(luò),服務(wù)器,go,rpc
使客戶端支持 tls 單向身份驗(yàn)證,ssl,網(wǎng)絡(luò),服務(wù)器,go,rpc

1、生成自簽證證書

CA 是一個(gè)受信任的實(shí)體,它管理和發(fā)布用于公共網(wǎng)絡(luò)中安全通信的安全證書和公鑰。由該受信任的實(shí)體所簽署或頒發(fā)的證書稱為 CA 簽名的證書。需要使用根證書CA來生成服務(wù)端、客戶端證書。
使客戶端支持 tls 單向身份驗(yàn)證,ssl,網(wǎng)絡(luò),服務(wù)器,go,rpc

使用 OpenSSL 開源工具集創(chuàng)建一個(gè) CA 根證書

根證書(root certificate)是屬于根證書頒發(fā)機(jī)構(gòu)(CA)的公鑰證書??梢酝ㄟ^驗(yàn)證 CA 的簽名從而信任 CA ,任何人都可以得到 CA 的證書(含公鑰),用以驗(yàn)證它所簽發(fā)的證書(客戶端、服務(wù)端)。

1、去openssl的官網(wǎng)下載openssl,安裝完成后,在終端運(yùn)行以下命令,可以看到相應(yīng)的版本號。

C:\Users\xxxxx>openssl -version
OpenSSL 3.2.1 30 Jan 2024 (Library: OpenSSL 3.2.1 30 Jan 2024)

2、在原有項(xiàng)目工程下,新建一個(gè)目錄cert目錄,進(jìn)入該目錄下,打開終端運(yùn)行以下命令:

openssl genrsa -out ca.key 2048

最終,在cert目錄將會生成ca.key的文件。

3、在cert目錄下,新建 ca.conf文件,并且寫入以下內(nèi)容:

[ req ]
default_bits       = 2048
distinguished_name = req_distinguished_name

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = CN
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = ChongQing
localityName                = Locality Name (eg, city)
localityName_default        = ChongQing
organizationName            = Organization Name (eg, company)
organizationName_default    = JiangShui
commonName                  = CommonName (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = jiangshui

然后運(yùn)行在終端運(yùn)行以下命令,創(chuàng)建ca證書簽發(fā)請求,得到ca.csr

openssl req -new -sha256 -out ca.csr -key ca.key -config ca.conf

如下圖所示,運(yùn)行命令后,填寫內(nèi)容均默認(rèn),全部按下enter即可,全部確認(rèn)完畢后,cert目錄下將會生成對應(yīng)ca.csr文件。

4、在終端下運(yùn)行以下命令,通過ca.key和ca.csr生成ca根證書,得到ca.crt

openssl x509 -req -days 3650 -in ca.csr -signkey ca.key -out ca.crt

2、單向認(rèn)證

生成服務(wù)端證書

1、運(yùn)行以下命令,生成服務(wù)端私鑰,server.key

openssl genrsa -out server.key 2048

2、在cert目錄下,新建server.conf文件,并且寫入以下內(nèi)容:

[ req ]
default_bits       = 2048
distinguished_name = req_distinguished_name

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = CN
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = ChongQing
localityName                = Locality Name (eg, city)
localityName_default        = ChongQing
organizationName            = Organization Name (eg, company)
organizationName_default    = JiangShui
commonName                  = CommonName (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = jiangshui

[ req_ext ]
# 添加subjectAltName
subjectAltName = @alt_names
# 文件末尾添加. www.p-pp.cn代表允許的ServerName,自己隨便寫
[alt_names]
DNS.1   = www.jiangshui.cn
DNS.2   = www.libai.cn
IP      = 127.0.0.1

然后運(yùn)行在終端運(yùn)行以下命令,創(chuàng)建證書簽發(fā)請求,得到server.csr

openssl req -new -sha256 -out server.csr -key server.key -config server.conf

3、在終端運(yùn)行以下命令,用CA證書和server.key、server.csr生成服務(wù)端證書(公鑰),得到server.pem

openssl x509 -req -days 3650 -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.pem -extensions req_ext -extfile server.conf

4、完成以上步驟后,查看cert目錄,是否存在以下圖片中所示的文件,如果對應(yīng)上,則說明生成各文件完畢。
使客戶端支持 tls 單向身份驗(yàn)證,ssl,網(wǎng)絡(luò),服務(wù)器,go,rpc

修改服務(wù)端代碼

修改后服務(wù)端grpc_server.go代碼如下:

package main

import (
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"grpc1/service"
	"net"
)

func main() {
	//引入TLS認(rèn)證相關(guān)文件(傳入公鑰和私鑰)
	//配置 TLS認(rèn)證相關(guān)文件
	creds, err := credentials.NewServerTLSFromFile("./cert/server.pem", "./cert/server.key")

	if err != nil {
		grpclog.Fatal("添加證書失敗", err)
	}

	//獲取grpc服務(wù)端,添加TLS認(rèn)證
	rpcServer := grpc.NewServer(grpc.Creds(creds))

	//服務(wù)端映射(注冊)到結(jié)構(gòu)體,調(diào)用product_grpc.pb.go中的RegisterProductServiceServer方法
	service.RegisterProductServiceServer(rpcServer, service.ProductService)
	// 建立tcp端口監(jiān)聽
	listener, err := net.Listen("tcp", ":8002")
	if err != nil {
		grpclog.Fatal("啟動監(jiān)聽出錯(cuò)", err)
	}
	//服務(wù)端監(jiān)聽tcp連接
	err = rpcServer.Serve(listener)
	if err != nil {
		grpclog.Fatal("啟動服務(wù)出錯(cuò)", err)
	}

}

如圖所示,此時(shí),啟動服務(wù)端代碼,然后啟動客戶端調(diào)用服務(wù)端方法查詢庫存,出現(xiàn)失敗。
使客戶端支持 tls 單向身份驗(yàn)證,ssl,網(wǎng)絡(luò),服務(wù)器,go,rpc

修改客戶端代碼

修改后服務(wù)端grpc_client.go代碼如下:

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"grpc1/service"
)

func main() {
	//先創(chuàng)建證書池,讀取并解析公開證書,創(chuàng)建啟用 TLS 的證書(公鑰域名)
	creds, err := credentials.NewClientTLSFromFile("./cert/server.pem", "www.jiangshui.cn")
	if err != nil {
		grpclog.Fatal("加載pem失敗", err)
	}
	//建立連接并添加傳輸憑證
	conn, err := grpc.Dial(":8002", grpc.WithTransportCredentials(creds))
	if err != nil {
		grpclog.Fatal("did not connect", err)
	}

	//關(guān)閉連接
	defer conn.Close()

	//調(diào)用product_grpc.pb.go中的NewProductServiceClient方法建立客戶端
	prodClient := service.NewProductServiceClient(conn)
	request := &service.ProductRequest{
		ProdId: 123,
	}

	//遠(yuǎn)程調(diào)用方法GetProductStock
	stockResponse, err := prodClient.GetProductStock(context.Background(), request)
	if err != nil {

		grpclog.Fatal("查詢庫存失敗", err)

	}
	fmt.Println("查詢成功", stockResponse.ProdStock)

}

如圖所示,此時(shí),啟動服務(wù)端代碼,然后啟動客戶端調(diào)用服務(wù)端方法查詢庫存,查詢成功,實(shí)現(xiàn)了一個(gè)單向認(rèn)證。
使客戶端支持 tls 單向身份驗(yàn)證,ssl,網(wǎng)絡(luò),服務(wù)器,go,rpc

3、雙向認(rèn)證

雙向認(rèn)證需要在單向認(rèn)證的基礎(chǔ)上,繼續(xù)生成客戶端證書,然后對客戶端和服務(wù)端代碼進(jìn)一步整改,才能完成雙向認(rèn)證。本人在實(shí)施雙向認(rèn)證的時(shí)候,也遇到了許多問題。當(dāng)我利用OpenSSL 開源工具集生成自簽證證書,然后按步驟生成完客戶端和服務(wù)端公私鑰,最后修改客戶端和服務(wù)端雙向認(rèn)證代碼并且運(yùn)行測試后,始終出現(xiàn)以下問題:

FATAL: 查詢庫存出錯(cuò)rpc error: code = Unavailable desc = connection error: desc = "transport: authentication handshake failed: tls: failed to verify certificate: 
x509: certificate signed by unknown authority (possibly because of \"x509: invalid signature: parent certificate cannot sign this kind of certificate\" while trying to verify candid
ate authority certificate \"jiangshui.com\")"

該錯(cuò)誤在我多次嘗試解決該bug,甚至直接搬用別人的代碼直接使用也未能解決!其中包括參考了許多博客的教程以及碼神之路的教程,都未能解決相關(guān)問題!

參考博客1:gRPC教程 — TLS單向認(rèn)證、雙向認(rèn)證、Token認(rèn)證、攔截器

參考博客2:【gRPC】自簽CA、服務(wù)端和客戶端雙向認(rèn)證

參考博客3:碼神之路教程

大家可以使用以上博客教程中的其中方法先進(jìn)行嘗試,看看如果能夠測試成功,自然皆大歡喜!如果測試失敗,出現(xiàn)與我相同錯(cuò)誤!接下來,我將提供一種與以上博客不同的方法來實(shí)現(xiàn)雙向認(rèn)證過程!與上述博客中描述的方法不同,此處我不在利用OpenSSL 開源工具集生成自簽證書以及服務(wù)端、客戶端的公私鑰。此處,我將直接使用go語言代碼和相關(guān)庫實(shí)現(xiàn)自簽證書以及服務(wù)端、客戶端的公私鑰生成。

用go代碼生成自簽證書和客戶端、服務(wù)端公私鑰

在cert文件目錄新建一個(gè)cert.go文件,并且在文件中寫入以下代碼:

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"net"
	"os"
	"time"
)

func main() {
	// 生成CA證書
	caCert, caPrivateKey, err := generateCACertificate()
	if err != nil {
		panic(err)
	}

	// 生成服務(wù)端證書
	err = generateCert("server.crt", "server.key", caCert, caPrivateKey, "localhost", true)
	if err != nil {
		panic(err)
	}

	// 生成客戶端證書
	err = generateCert("client.crt", "client.key", caCert, caPrivateKey, "Client", false)
	if err != nil {
		panic(err)
	}
}

func generateCACertificate() (*x509.Certificate, *ecdsa.PrivateKey, error) {
	caPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return nil, nil, err
	}

	serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
	if err != nil {
		return nil, nil, err
	}

	caTemplate := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Example CA"},
		},
		NotBefore:             time.Now(),
		NotAfter:              time.Now().AddDate(10, 0, 0), // 10 years
		KeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
		BasicConstraintsValid: true,
		IsCA:                  true,
	}

	caCertBytes, err := x509.CreateCertificate(rand.Reader, &caTemplate, &caTemplate, &caPrivateKey.PublicKey, caPrivateKey)
	if err != nil {
		return nil, nil, err
	}

	caCert, err := x509.ParseCertificate(caCertBytes)
	if err != nil {
		return nil, nil, err
	}

	// Save the CA certificate
	saveCert("ca.crt", caCertBytes)
	// Save the CA private key
	savePrivateKey("ca.key", caPrivateKey)

	return caCert, caPrivateKey, nil
}

func generateCert(certFilename, keyFilename string, caCert *x509.Certificate, caPrivateKey *ecdsa.PrivateKey, commonName string, isServer bool) error {
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return err
	}

	serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
	if err != nil {
		return err
	}

	certTemplate := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			CommonName:   commonName,
			Organization: []string{"Example Org"},
		},
		NotBefore:             time.Now(),
		NotAfter:              time.Now().AddDate(1, 0, 0), // 1 year
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
		BasicConstraintsValid: true,
	}

	// 為證書添加SANs字段
	certTemplate.DNSNames = append(certTemplate.DNSNames, "localhost")
	certTemplate.IPAddresses = append(certTemplate.IPAddresses, net.ParseIP("127.0.0.1"))

	if isServer {
		certTemplate.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
	}

	certBytes, err := x509.CreateCertificate(rand.Reader, &certTemplate, caCert, &privateKey.PublicKey, caPrivateKey)
	if err != nil {
		return err
	}

	saveCert(certFilename, certBytes)
	savePrivateKey(keyFilename, privateKey)

	return nil
}

func saveCert(filename string, certBytes []byte) {
	certPEMFile, err := os.Create(filename)
	if err != nil {
		panic(err)
	}
	defer certPEMFile.Close()

	pem.Encode(certPEMFile, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes})
}

func savePrivateKey(filename string, privateKey *ecdsa.PrivateKey) {
	keyFile, err := os.Create(filename)
	if err != nil {
		panic(err)
	}
	defer keyFile.Close()

	privBytes, err := x509.MarshalECPrivateKey(privateKey)
	if err != nil {
		panic(err)
	}
	pem.Encode(keyFile, &pem.Block{Type: "EC PRIVATE KEY", Bytes: privBytes})
}

代碼中的caTemplate、certTemplate部分類似于OpenSSL 開源工具集生成證書,相關(guān)部分可以根據(jù)自己的需求進(jìn)行修改!此外,證書保存的路徑根據(jù)自己的具體情況,進(jìn)行修改!或者生成完證書后,自己將證書文件移動到對應(yīng)的目錄下。
使客戶端支持 tls 單向身份驗(yàn)證,ssl,網(wǎng)絡(luò),服務(wù)器,go,rpc
此處圖片中的內(nèi)容則是類似于OpenSSL 開源工具集生成SAN證書部分,添加SANs字段,DNSName可以根據(jù)自身需求填寫(添加多個(gè))。

修改服務(wù)端代碼

server_grpc.go

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"google.golang.org/grpc/grpclog"
	"grpc2/service"
	"io/ioutil"
	"net"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

func main() {
	// 證書認(rèn)證-雙向認(rèn)證
	// 從證書相關(guān)文件中讀取和解析信息,得到證書公鑰、密鑰對
	cert, err := tls.LoadX509KeyPair("./cert/server.crt", "./cert/server.key")
	if err != nil {
		grpclog.Fatalf("failed to load server certificate: %v", err)
	}

	caCert, err := ioutil.ReadFile("./cert/ca.crt")
	if err != nil {
		grpclog.Fatalf("failed to read ca certificate: %v", err)
	}
	// 創(chuàng)建一個(gè)新的、空的證書池
	caCertPool := x509.NewCertPool()
	// 附加來自 CA 的證書
	if !caCertPool.AppendCertsFromPEM(caCert) {
		grpclog.Fatalf("failed to append ca certs")
	}
	// 構(gòu)建基于 TLS 的 TransportCredentials 選項(xiàng)
	config := &tls.Config{
		Certificates: []tls.Certificate{cert},        // 服務(wù)器的證書和私鑰
		ClientAuth:   tls.RequireAndVerifyClientCert, //啟用并要求客戶端證書驗(yàn)證
		ClientCAs:    caCertPool,                     //用于驗(yàn)證客戶端證書的 CA 證書
	}

	listener, err := net.Listen("tcp", ":50051")
	if err != nil {
		grpclog.Fatalf("failed to listen: %v", err)
	}

	rpcServer := grpc.NewServer(grpc.Creds(credentials.NewTLS(config)))
	// 在此注冊您的 gRPC 服務(wù)處理程序

	service.RegisterProductServiceServer(rpcServer, service.ProductService)

	if err != nil {
		grpclog.Fatal("啟動監(jiān)聽出錯(cuò)", err)
	}
	err = rpcServer.Serve(listener)
	if err != nil {
		grpclog.Fatal("啟動服務(wù)出錯(cuò)", err)
	}
	fmt.Println("啟動grpc服務(wù)端成功")
}

修改客戶端代碼

client_grpc.go

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"grpc2/service"
	"io/ioutil"
)

func main() {
	// 證書認(rèn)證-雙向認(rèn)證
	// 從證書相關(guān)文件中讀取和解析信息,得到證書公鑰、密鑰對
	clientCert, err := tls.LoadX509KeyPair("./cert/client.crt", "./cert/client.key")
	if err != nil {
		grpclog.Fatalf("failed to load client certificate: %v", err)
	}

	// 創(chuàng)建一個(gè)新的、空的證書池
	certPool := x509.NewCertPool()
	ca, err := ioutil.ReadFile("./cert/ca.crt")
	if err != nil {
		grpclog.Fatalf("could not read ca certificate: %v", err)
	}

	// 附加來自 CA 的證書
	if ok := certPool.AppendCertsFromPEM(ca); !ok {
		grpclog.Fatalf("failed to append ca certs")
	}
	// 構(gòu)建基于 TLS 的 TransportCredentials 選項(xiàng)
	config := &tls.Config{
		Certificates: []tls.Certificate{clientCert}, // 客戶端的證書和私鑰
		ServerName:   "localhost",                   //驗(yàn)證DNSName
		RootCAs:      certPool,                      // CA證書用于驗(yàn)證服務(wù)器證書
	}

	conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(credentials.NewTLS(config)))
	if err != nil {
		grpclog.Fatalf("failed to dial server: %v", err)
	}
	defer conn.Close()

	// 現(xiàn)在您可以使用 `conn` 創(chuàng)建客戶端并進(jìn)行 gRPC 調(diào)用
	prodClient := service.NewProductServiceClient(conn)

	request := &service.ProductRequest{
		ProdId: 123,
	}
	stockResponse, err := prodClient.GetProductStock(context.Background(), request)
	if err != nil {
		grpclog.Fatal("查詢庫存出錯(cuò)", err)
	}
	fmt.Println("查詢成功", stockResponse.ProdStock)
}

客戶端和服務(wù)端代碼修改完成后,啟動服務(wù)端,運(yùn)行客戶端進(jìn)行測試,此時(shí)不再出現(xiàn)之前所遇到的錯(cuò)誤。如下圖所示,成功調(diào)用到獲取庫存的方法,查詢庫存成功!
使客戶端支持 tls 單向身份驗(yàn)證,ssl,網(wǎng)絡(luò),服務(wù)器,go,rpc

4、token認(rèn)證

gRPC為每個(gè)gRPC方法調(diào)用提供了Token認(rèn)證支持,可以基于用戶傳入的Token判斷用戶是否登陸、以及權(quán)限等。

服務(wù)端添加用戶名密碼的校驗(yàn)

server_grpc.go

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
	"grpc2/service"
	"io/ioutil"
	"net"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

func main() {
	// 證書認(rèn)證-雙向認(rèn)證
	// 從證書相關(guān)文件中讀取和解析信息,得到證書公鑰、密鑰對
	cert, err := tls.LoadX509KeyPair("./cert/server.crt", "./cert/server.key")
	if err != nil {
		grpclog.Fatalf("failed to load server certificate: %v", err)
	}

	caCert, err := ioutil.ReadFile("./cert/ca.crt")
	if err != nil {
		grpclog.Fatalf("failed to read ca certificate: %v", err)
	}
	// 創(chuàng)建一個(gè)新的、空的證書池
	caCertPool := x509.NewCertPool()
	// 附加來自 CA 的證書
	if !caCertPool.AppendCertsFromPEM(caCert) {
		grpclog.Fatalf("failed to append ca certs")
	}
	// 構(gòu)建基于 TLS 的 TransportCredentials 選項(xiàng)
	config := &tls.Config{
		Certificates: []tls.Certificate{cert},        // 服務(wù)器的證書和私鑰
		ClientAuth:   tls.RequireAndVerifyClientCert, //啟用并要求客戶端證書驗(yàn)證
		ClientCAs:    caCertPool,                     //用于驗(yàn)證客戶端證書的 CA 證書
	}

	listener, err := net.Listen("tcp", ":50051")
	if err != nil {
		grpclog.Fatalf("failed to listen: %v", err)
	}

	//實(shí)現(xiàn)token認(rèn)證,需要合法的用戶名和密碼
	//實(shí)現(xiàn)一個(gè)攔截器
	var authInterceptor grpc.UnaryServerInterceptor
	authInterceptor = func(
		ctx context.Context,
		req any,
		info *grpc.UnaryServerInfo,
		handler grpc.UnaryHandler,
	) (resp any, err error) {
		//攔截普通方法請求,驗(yàn)證Token
		err = Auth(ctx)
		if err != nil {
			return
		}
		return handler(ctx, req)
	}

	//添加在服務(wù)端攔截器
	rpcServer := grpc.NewServer(grpc.Creds(credentials.NewTLS(config)), grpc.UnaryInterceptor(authInterceptor))
	// 在此注冊您的 gRPC 服務(wù)處理程序

	service.RegisterProductServiceServer(rpcServer, service.ProductService)

	if err != nil {
		grpclog.Fatal("啟動監(jiān)聽出錯(cuò)", err)
	}
	err = rpcServer.Serve(listener)
	if err != nil {
		grpclog.Fatal("啟動服務(wù)出錯(cuò)", err)
	}
	fmt.Println("啟動grpc服務(wù)端成功")
}

func Auth(ctx context.Context) error {
	//拿到傳輸?shù)挠脩裘兔艽a
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return fmt.Errorf("missing credentials")
	}
	var user string
	var password string

	if val, ok := md["user"]; ok {
		user = val[0]
	}
	if val, ok := md["password"]; ok {
		password = val[0]
	}

	if user != "admin" || password != "admin" {
		return status.Errorf(codes.Unauthenticated, "token不合法")
	}

	return nil
}

此時(shí),啟動客戶端遠(yuǎn)程調(diào)用方法失敗,出現(xiàn)token不合法的提示,如下圖所示
使客戶端支持 tls 單向身份驗(yàn)證,ssl,網(wǎng)絡(luò),服務(wù)器,go,rpc

修改客戶端代碼,以適應(yīng)token認(rèn)證

客戶段需要攜帶token進(jìn)行傳入,使得服務(wù)端能進(jìn)行正確驗(yàn)證,因此需要修改客戶端代碼,其中主要修改以下代碼:

//客戶端攜帶token傳入
conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(credentials.NewTLS(config)), grpc.WithPerRPCCredentials(token))

通過grpc.WithPerRPCCredentials(token)函數(shù)攜帶token。通過追蹤該函數(shù),可得知該函數(shù)接收的參數(shù)為一個(gè)接口,因此客戶端傳入的token,需要定義一個(gè)結(jié)構(gòu)體將數(shù)據(jù)傳入,并且將該結(jié)構(gòu)體需要實(shí)現(xiàn)grpc.PerRPCCredentials接口。

type PerRPCCredentials interface {
	// 返回需要認(rèn)證的必要信息
	GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
  // 是否使用安全鏈接(TLS)
	RequireTransportSecurity() bool
}

實(shí)現(xiàn)grpc.PerRPCCredentials接口

在原來項(xiàng)目的基礎(chǔ)上,新建auth文件目錄,并且新建auth.go文件,寫入以下代碼實(shí)現(xiàn)相關(guān)接口:

package auth

import "context"

type Authentication struct {
	User     string
	Password string
}

func (a *Authentication) GetRequestMetadata(context.Context, ...string) (
	map[string]string, error,
) {
	return map[string]string{"user": a.User, "password": a.Password}, nil
}

func (a *Authentication) RequireTransportSecurity() bool {
	return false
}

修改客戶端代碼

client_grpc.go

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"grpc2/grpc_client/auth"
	"grpc2/service"
	"io/ioutil"
)

func main() {
	// 證書認(rèn)證-雙向認(rèn)證
	// 從證書相關(guān)文件中讀取和解析信息,得到證書公鑰、密鑰對
	clientCert, err := tls.LoadX509KeyPair("./cert/client.crt", "./cert/client.key")
	if err != nil {
		grpclog.Fatalf("failed to load client certificate: %v", err)
	}

	// 創(chuàng)建一個(gè)新的、空的證書池
	certPool := x509.NewCertPool()
	ca, err := ioutil.ReadFile("./cert/ca.crt")
	if err != nil {
		grpclog.Fatalf("could not read ca certificate: %v", err)
	}

	// 附加來自 CA 的證書
	if ok := certPool.AppendCertsFromPEM(ca); !ok {
		grpclog.Fatalf("failed to append ca certs")
	}
	// 構(gòu)建基于 TLS 的 TransportCredentials 選項(xiàng)
	config := &tls.Config{
		Certificates: []tls.Certificate{clientCert}, // 客戶端的證書和私鑰
		ServerName:   "localhost",                   //驗(yàn)證DNSName
		RootCAs:      certPool,                      // CA證書用于驗(yàn)證服務(wù)器證書
	}
	token := &auth.Authentication{
		User:     "admin",
		Password: "admin",
	}
	//客戶端攜帶token傳入
	conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(credentials.NewTLS(config)), grpc.WithPerRPCCredentials(token))
	if err != nil {
		grpclog.Fatalf("failed to dial server: %v", err)
	}
	defer conn.Close()

	// 現(xiàn)在您可以使用 `conn` 創(chuàng)建客戶端并進(jìn)行 gRPC 調(diào)用
	prodClient := service.NewProductServiceClient(conn)

	request := &service.ProductRequest{
		ProdId: 123,
	}
	stockResponse, err := prodClient.GetProductStock(context.Background(), request)
	if err != nil {
		grpclog.Fatal("查詢庫存出錯(cuò)", err)
	}
	fmt.Println("查詢成功", stockResponse.ProdStock)
}

最后,啟動服務(wù)端,運(yùn)行客戶端代碼進(jìn)行測試。當(dāng)攜帶的token與服務(wù)端要求的不一致時(shí),出現(xiàn)下圖所示的token不合法,查詢失敗。
使客戶端支持 tls 單向身份驗(yàn)證,ssl,網(wǎng)絡(luò),服務(wù)器,go,rpc

當(dāng)攜帶的token與服務(wù)端要求的一致時(shí),出現(xiàn)下圖所示查詢成功!使客戶端支持 tls 單向身份驗(yàn)證,ssl,網(wǎng)絡(luò),服務(wù)器,go,rpc文章來源地址http://www.zghlxwxcb.cn/news/detail-845754.html

到了這里,關(guān)于gRPC — SSL/TLS單向認(rèn)證、雙向認(rèn)證、Token認(rèn)證的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • JAVA使用RestTemplate類實(shí)現(xiàn)SSL雙向/單向認(rèn)證(國際)

    JAVA使用RestTemplate類實(shí)現(xiàn)SSL雙向/單向認(rèn)證(國際)

    以管理員身份打開Windows PowerShel,通過cd(與linux系統(tǒng)類似)命令進(jìn)入到JDK的bin目錄:如C:Program FilesJavajdk1.8.0_221jrebin,找到目錄下有keytool.exe就是正確進(jìn)入目錄了 參數(shù)說明: genkey 表示要創(chuàng)建一個(gè)新的密鑰 alias 表示 keystore 的別名、 keyalg 表示使用的加密算法是 RSA ,一種非

    2024年02月15日
    瀏覽(21)
  • openssl自簽名CA根證書、服務(wù)端和客戶端證書生成并模擬單向/雙向證書驗(yàn)證

    1.1 生成CA證書私鑰 openssl genrsa -aes256 -out ca.key 2048 1.2 取消密鑰的密碼保護(hù) openssl rsa -in ca.key -out ca.key 1.3 生成根證書簽發(fā)申請文件(csr文件) openssl req -new -sha256 -key ca.key -out ca.csr -subj \\\"/C=CN/ST=FJ/L=XM/O=NONE/OU=NONE/CN=localhost/emailAddress=test@test.com\\\" 上述參數(shù)含義 req----執(zhí)行證書簽發(fā)命令

    2024年04月25日
    瀏覽(26)
  • emqx 配置ssl/tls 雙向認(rèn)證(親自測試有效)

    emqx 配置ssl/tls 雙向認(rèn)證(親自測試有效)

    bash腳本,生成自簽名ca、服務(wù)端、客戶端的key和證書 openssl.cnf配置文件 驗(yàn)證證書是否有效 將證書文件拷貝到emqxetccerts目錄下(默認(rèn)目錄),并修改配置文件emqx.conf。SSL/TLS 雙向連接的啟用及驗(yàn)證 mqttx連接驗(yàn)證 出現(xiàn)連接成功,代表測試無問題 ?

    2024年03月11日
    瀏覽(24)
  • 基于SSL/TLS雙向安全連接設(shè)備CA證書認(rèn)證

    基于SSL/TLS雙向安全連接設(shè)備CA證書認(rèn)證

    小伙伴兒們,如果覺得文章干貨滿滿,歡迎加入公眾號【編程識堂】,更多干貨等你們來哦! 設(shè)備證書是由CA根證書簽發(fā)給客戶端設(shè)備使用的數(shù)字證書,用于客戶端和服務(wù)端連接時(shí),服務(wù)端對客戶端進(jìn)行安全認(rèn)證。認(rèn)證通過后服務(wù)端和客戶端可基于證書內(nèi)的加密密鑰進(jìn)行安全

    2024年01月20日
    瀏覽(40)
  • Kubernetes客戶端認(rèn)證——基于CA證書的雙向認(rèn)證方式

    Kubernetes客戶端認(rèn)證——基于CA證書的雙向認(rèn)證方式

    Kubernetes集群的訪問權(quán)限控制由API Server負(fù)責(zé),API Server的訪問權(quán)限控制由身份驗(yàn)證(Authentication)、授權(quán)(Authorization)和準(zhǔn)入控制(Admission control)三個(gè)步驟組成,這個(gè)三個(gè)步驟是按序進(jìn)行的(詳細(xì)介紹請參見《(轉(zhuǎn))使用kubectl訪問Kubernetes集群時(shí)的身份驗(yàn)證和授權(quán)》)。 其中身份驗(yàn)證

    2023年04月10日
    瀏覽(22)
  • [Kafka集群] 配置支持Brokers內(nèi)部SSL認(rèn)證\外部客戶端支持SASL_SSL認(rèn)證并集成spring-cloud-starter-bus-kafka

    [Kafka集群] 配置支持Brokers內(nèi)部SSL認(rèn)證\外部客戶端支持SASL_SSL認(rèn)證并集成spring-cloud-starter-bus-kafka

    目錄 Kafka 集群配置 準(zhǔn)備 配置流程 Jaas(Java Authentication and Authorization Service?)文件 zookeeper 配置文件 SSL自簽名 啟動zookeeper集群 啟動kafka集群? spring-cloud-starter-bus-kafka 集成 下載統(tǒng)一版本Kafka服務(wù)包至三臺不同的服務(wù)器上 文章使用版本為? kafka_2.13-3.5.0.tgz 下載地址 jdk版本 為 Ado

    2024年02月04日
    瀏覽(99)
  • HTTPS單向認(rèn)證與雙向認(rèn)證

    Https就是HTTP+SSL/TSL的簡稱。 SSL(Secure Socket Layer 安全套接層)是TCP/IP協(xié)議中基于HTTP之下TCP之上的一個(gè)可選協(xié)議層。 起初HTTP在傳輸數(shù)據(jù)時(shí)使用的是明文,傳輸過程中并不安全。網(wǎng)景(Netscap)公司推出了SSL解決了這一安全隱患,因此越來越多的人也開始使用HTTPS。在SSL更新到3.0時(shí),

    2024年02月11日
    瀏覽(21)
  • 【網(wǎng)絡(luò)】https單向認(rèn)證和雙向認(rèn)證

    【網(wǎng)絡(luò)】https單向認(rèn)證和雙向認(rèn)證

    之前面試的時(shí)候,面試官問我了解過https的雙向認(rèn)證嗎?當(dāng)時(shí),的確不理解。不過沒關(guān)系,現(xiàn)在就來補(bǔ)上。 1.單向認(rèn)證 還是有必要先說下單向認(rèn)證,單向認(rèn)證是我剛開始接觸https的時(shí)候就了解到的。下面看一下執(zhí)行流程圖 (圖是網(wǎng)上找的) 再用文字描述下: 首先建立鏈接? - 驗(yàn)

    2023年04月09日
    瀏覽(17)
  • NGINX自建SSL實(shí)現(xiàn)-單向、雙向加密,中文SSL證書,吊銷列表實(shí)現(xiàn)

    對稱加密算法 對稱加密算法,同一個(gè)密鑰可以同時(shí)用作信息的加密和解密,這種加密方法稱為對稱加密,也稱為單密鑰加密。 非對稱加密算法 非對稱加密算法(RSA)是內(nèi)容加密的一類算法,它有兩個(gè)秘鑰:公鑰與私鑰。公鑰是公開的鑰匙,所有人都可以知道,私鑰是保密的

    2024年02月03日
    瀏覽(22)
  • ssl單向證書和雙向證書校驗(yàn)測試及搭建流程

    首先了解下HTTP和HTTPS的區(qū)別: HTTPS與HTTP有什么不同? HTTP是過去很長一段時(shí)間我們經(jīng)常用到的一種傳輸協(xié)議。HTTP協(xié)議傳輸?shù)臄?shù)據(jù)都是未加密的,這就意味著用戶填寫的密碼、賬號、交易記錄等機(jī)密信息都是明文,隨時(shí)可能被泄露、竊取、篡改,從而被黑客加以利用,因此使用

    2024年02月13日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包