grpc介绍(二)——认证方式

前言

HTTP是明文传输的,即客户端与服务端之间通信的信息是可见的,这就存在被窃听、冒充或篡改的风险。HTTPS在HTTP和TCP之间加入了TLS协议,如图所示:

img

TLS协议主要解决了以下三个网络安全问题:

  • 信息加密: HTTP 交互信息是被加密的,第三方就无法被窃取;
  • 校验机制:校验信息传输过程中是否有被第三方篡改过,如果被篡改过,则会有警告提示;
  • 身份证书:双方认证,双方都可以配置证书,防止身份被冒充;

客户端与服务端通过gRPC进行方法调用,也需要加入证书来保证调用的安全。

生成自签证书

安装openssl

# 下载openssl --- Win64 OpenSSL v3.0.5
https://slproweb.com/products/Win32OpenSSL.html

# 添加环境变量,将openssl.exe所在路径加入系统环境变量
C:\Program Files\OpenSSL-Win64\bin

生成私钥文件

openssl genrsa -des3 -out ca.key 2048

创建证书请求

openssl req -new -key ca.key -out ca.csr

生成ca.crt

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

修改openssl.cnf

# 打开copy_extensions
copy_extensions = copy

# 打开req_extensions
req_extensions = v3_req

# 找到[ v3_req ], 添加
subjectAltName = @alt_names

# 添加标签
[ alt_names ]
DNS.1 = *.dong.com

生成证书私钥

openssl genpkey -algorithm RSA -out server.key

通过私钥生成证书请求文件

openssl req -new -nodes -key server.key -out server.csr -days 3650 -config ./openssl.cnf -extensions v3_req

生成SAN证书

openssl x509 -req -days 365 -in server.csr -out server.pem -CA ca.crt -CAkey ca.key -CAc
reateserial -extfile ./openssl.cnf -extensions v3_req

说明:

  • key:服务器上的私钥文件,用于对发送给客户端数据的加密,以及对从客户端接收到数据的解密;
  • csr:证书签名请求文件,用于提交给证书颁发机构(CA)对证书签名;
  • crt:由颁发证书机构(CA)签名后的证书,或者是开发者自签名的证书,包含证书持有人的信息,持有人的公钥,以及签署者的签名等信息;
  • pem:是基于Base64编码的证书格式,扩展名包括PEM、CRT和CER;

单向认证

服务端修改

package main

import (
	"service"

	"google.golang.org/grpc"

	"net"

	"log"

	"fmt"

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

func main() {
	fmt.Println("开始启动服务")

	// 添加证书
	creds, err0 := credentials.NewServerTLSFromFile("cert/server.pem", "cert/server.key")
	if err0 != nil {
		log.Fatal("证书生成失败", err0)
	}

    // 创建rpc实例(添加认证)
	rpcServer := grpc.NewServer(grpc.Creds(creds))

	// 服务注册
	service.RegisterProdServiceServer(rpcServer, service.ProductService)

	// 启动监听
	listener, err := net.Listen("tcp", ":8800")
	if err != nil {
		log.Fatal("启动监听失败", err)
	}

	// 启动服务
	err = rpcServer.Serve(listener)
	if err != nil {
		log.Fatal("启动服务失败", err)
	}

	fmt.Println("启动服务成功")
}

服务端启动

在这里插入图片描述

客户端修改

package main

import (
	"google.golang.org/grpc"

	"log"

	"service"

	"context"

	"fmt"

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

func main() {
	// 添加公钥
	creds, err0 := credentials.NewClientTLSFromFile("../cert/server.pem", "*.dong.com")
	if err0 != nil {
		log.Fatal("证书错误: ", err0)
	}

	// 创建连接
	conn, err := grpc.Dial(":8800", grpc.WithTransportCredentials(creds))
	if err != nil {
		log.Fatal("服务端连接失败: ", err)
	}
    
    fmt.Println("证书认证通过")

	// 退出时关闭连接
	defer conn.Close()

	// 创建客户端实例
	productServiceClient := service.NewProdServiceClient(conn)

	// 方法请求
	resq, err := productServiceClient.GetProductStock(context.Background(), &service.ProductRequest{ProdId: 233})
	if err != nil {
		log.Fatal("调用gRPC方法失败: ", err)
	}

	fmt.Println("调用gRPC方法成功, ProdStock = ", resq.ProdStock)
}

客户端启动

在这里插入图片描述

单向认证流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NB1Wr3o9-1653889513788)(img/1586953-20210625171059706-1447106002-16509094111532.png)]

抓包分析

在这里插入图片描述

工程框架

在这里插入图片描述

双向认证

生成客户端公钥和私钥

# 生成私钥
openssl genpkey -algorithm RSA -out client.key

# 生成证书
openssl req -new -nodes -key client.key -out client.csr -days 3650 -config ./openssl.cnf -extensions v3_req

# 生成SAN证书
openssl x509 -req -days 365 -in client.csr -out client.pem -CA ca.crt -CAkey ca.key -CAcreateserial -extfile ./openssl.cnf -extensions v3_req

服务端修改

package main

import (
	"service"

	"google.golang.org/grpc"

	"net"

	"log"

	"fmt"

	"google.golang.org/grpc/credentials"

	"crypto/tls"

	"crypto/x509"

	"io/ioutil"
)

func main() {
	fmt.Println("开始启动服务")

	// 添加证书
	// creds, err0 := credentials.NewServerTLSFromFile("cert/server.pem", "cert/server.key")
	// if err0 != nil {
	// 	log.Fatal("证书生成失败", err0)
	// }

	// 证书认证-双向认证
	cert, err0 := tls.LoadX509KeyPair("cert/server.pem", "cert/server.key")
	if err0 != nil {
		log.Fatal("证书读取失败", err0)
	}

	fmt.Println("证书读取成功")

	// 创建一个新的、空的CertPool
	certPool := x509.NewCertPool()
	ca, err1 := ioutil.ReadFile("cert/ca.crt")
	if err1 != nil {
		log.Fatal("ca证书读取失败", err1)
	}

	fmt.Println("ca证书读取成功")

	// 尝试解析所传入的PEM编码的证书,如果解析成功会将其加到CertPool中,便于后面使用
	certPool.AppendCertsFromPEM(ca)
	// 构建基于TLS的TransportCredentials选项
	creds := credentials.NewTLS(&tls.Config{
		// 设置证书链, 允许包含一个或多个
		Certificates: []tls.Certificate{cert},
		// 要求必须校验客户端的证书
		ClientAuth: tls.RequireAndVerifyClientCert,
		// 设置根证书的集合, 校验方式使用ClientAuth中设定的模式
		ClientCAs: certPool,
	})

	fmt.Println("设置TLS的TransportCredentials选项成功")

	// 创建rpc实例
	rpcServer := grpc.NewServer(grpc.Creds(creds))

	// 服务注册
	service.RegisterProdServiceServer(rpcServer, service.ProductService)

	// 启动监听
	listener, err := net.Listen("tcp", ":8800")
	if err != nil {
		log.Fatal("启动监听失败", err)
	}

	// 启动服务
	err = rpcServer.Serve(listener)
	if err != nil {
		log.Fatal("启动服务失败", err)
	}

	fmt.Println("启动服务成功")
}

服务端启动

在这里插入图片描述

客户端修改

package main

import (
	"google.golang.org/grpc"

	"log"

	"service"

	"context"

	"fmt"

	"google.golang.org/grpc/credentials"

	"crypto/tls"

	"crypto/x509"

	"io/ioutil"
)

func main() {
	// 添加公钥
	// creds, err0 := credentials.NewClientTLSFromFile("../cert/server.pem", "*.dong.com")
	// if err0 != nil {
	// 	log.Fatal("证书错误: ", err0)
	// }

	// 证书认证-双向认证
	// 从证书相关文件中读取解析信息, 得到证书公钥、密钥对
	cert, err0 := tls.LoadX509KeyPair("../cert/client.pem", "../cert/client.key")
	if err0 != nil {
		log.Fatal("证书读取失败", err0)
	}

	fmt.Println("证书读取成功")

	// 创建一个新的、空的CertPool
	certPool := x509.NewCertPool()
	ca, err1 := ioutil.ReadFile("../cert/ca.crt")
	if err1 != nil {
		log.Fatal("ca证书读取失败", err1)
	}

	fmt.Println("ca证书读取成功")

	// 尝试解析所传入的PEM编码的证书,如果解析成功会将其加到CertPool中,便于后面使用
	certPool.AppendCertsFromPEM(ca)
	// 构建基于TLS的TransportCredentials选项
	creds := credentials.NewTLS(&tls.Config{
		// 设置证书链, 允许包含一个或多个
		Certificates: []tls.Certificate{cert},
		ServerName:   "*.dong.com",
		RootCAs:      certPool,
	})

	fmt.Println("设置TLS的TransportCredentials选项成功")

	// 创建连接
	conn, err := grpc.Dial(":8800", grpc.WithTransportCredentials(creds))
	if err != nil {
		log.Fatal("服务端连接失败: ", err)
	}

	fmt.Println("证书认证通过")

	// 退出时关闭连接
	defer conn.Close()

	// 创建客户端实例
	productServiceClient := service.NewProdServiceClient(conn)

	// 方法请求
	resq, err := productServiceClient.GetProductStock(context.Background(), &service.ProductRequest{ProdId: 233})
	if err != nil {
		log.Fatal("调用gRPC方法失败: ", err)
	}

	fmt.Println("调用gRPC方法成功, ProdStock = ", resq.ProdStock)
}

客户端启动

在这里插入图片描述

成功执行。

双向认证流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CEOuQHmt-1653889513789)(img/1586953-20210625211235069-195172761-16509094417774.png)]

抓包分析

在这里插入图片描述

工程框架

在这里插入图片描述

Token认证

服务端修改

package main

import (
	"service"

	"google.golang.org/grpc"

	"net"

	"log"

	"fmt"

	"google.golang.org/grpc/credentials"

	"crypto/tls"

	"crypto/x509"

	"io/ioutil"

	"context"

	"google.golang.org/grpc/metadata"

	"google.golang.org/grpc/status"

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

func main() {
	fmt.Println("开始启动服务")

	// 添加证书
	// creds, err0 := credentials.NewServerTLSFromFile("cert/server.pem", "cert/server.key")
	// if err0 != nil {
	// 	log.Fatal("证书生成失败", err0)
	// }

	// 证书认证-双向认证
	cert, err0 := tls.LoadX509KeyPair("cert/server.pem", "cert/server.key")
	if err0 != nil {
		log.Fatal("证书读取失败", err0)
	}

	fmt.Println("证书读取成功")

	// 创建一个新的、空的CertPool
	certPool := x509.NewCertPool()
	ca, err1 := ioutil.ReadFile("cert/ca.crt")
	if err1 != nil {
		log.Fatal("ca证书读取失败", err1)
	}

	fmt.Println("ca证书读取成功")

	// 尝试解析所传入的PEM编码的证书,如果解析成功会将其加到CertPool中,便于后面使用
	certPool.AppendCertsFromPEM(ca)
	// 构建基于TLS的TransportCredentials选项
	creds := credentials.NewTLS(&tls.Config{
		// 设置证书链, 允许包含一个或多个
		Certificates: []tls.Certificate{cert},
		// 要求必须校验客户端的证书
		ClientAuth: tls.RequireAndVerifyClientCert,
		// 设置根证书的集合, 校验方式使用ClientAuth中设定的模式
		ClientCAs: certPool,
	})

	fmt.Println("设置TLS的TransportCredentials选项成功")

	// token认证 -- 合法的用户名和密码
	var authInterceptor grpc.UnaryServerInterceptor
	authInterceptor = func(
		ctx context.Context,
		req interface{},
		info *grpc.UnaryServerInfo,
		handler grpc.UnaryHandler,
	) (resp interface{}, err error) {
		// 拦截请求, 验证token
		err = Auth(ctx)
		if err != nil {
			return
		}

		// 继续处理请求
		return handler(ctx, req)
	}

	// 创建rpc实例
	rpcServer := grpc.NewServer(grpc.Creds(creds), grpc.UnaryInterceptor(authInterceptor))

	// 服务注册
	service.RegisterProdServiceServer(rpcServer, service.ProductService)

	// 启动监听
	listener, err := net.Listen("tcp", ":8800")
	if err != nil {
		log.Fatal("启动监听失败", err)
	}

	// 启动服务
	err = rpcServer.Serve(listener)
	if err != nil {
		log.Fatal("启动服务失败", err)
	}

	fmt.Println("启动服务成功")
}

func Auth(ctx context.Context) error {
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return fmt.Errorf("missing credentials")
	}

	var user string
	var passwd string

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

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

	if user != "admin" || passwd != "admin@123" {
		return status.Errorf(codes.Unauthenticated, "token认证失败")
	}

	fmt.Println("token认证成功")
	return nil
}

添加认证接口

// 新增auth.go文件

package auth

import "context"

type Authentication struct {
	User   string
	Passwd string
}

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

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

客户端修改

package main

import (
	"google.golang.org/grpc"

	"log"

	"service"

	"context"

	"fmt"

	"google.golang.org/grpc/credentials"

	"crypto/tls"

	"crypto/x509"

	"io/ioutil"

	"auth"
)

func main() {
	// 添加公钥
	// creds, err0 := credentials.NewClientTLSFromFile("../cert/server.pem", "*.dong.com")
	// if err0 != nil {
	// 	log.Fatal("证书错误: ", err0)
	// }

	// 证书认证-双向认证
	// 从证书相关文件中读取解析信息, 得到证书公钥、密钥对
	cert, err0 := tls.LoadX509KeyPair("../cert/client.pem", "../cert/client.key")
	if err0 != nil {
		log.Fatal("证书读取失败", err0)
	}

	fmt.Println("证书读取成功")

	// 创建一个新的、空的CertPool
	certPool := x509.NewCertPool()
	ca, err1 := ioutil.ReadFile("../cert/ca.crt")
	if err1 != nil {
		log.Fatal("ca证书读取失败", err1)
	}

	fmt.Println("ca证书读取成功")

	// 尝试解析所传入的PEM编码的证书,如果解析成功会将其加到CertPool中,便于后面使用
	certPool.AppendCertsFromPEM(ca)
	// 构建基于TLS的TransportCredentials选项
	creds := credentials.NewTLS(&tls.Config{
		// 设置证书链, 允许包含一个或多个
		Certificates: []tls.Certificate{cert},
		ServerName:   "*.dong.com",
		RootCAs:      certPool,
	})

	fmt.Println("设置TLS的TransportCredentials选项成功")

	token := &auth.Authentication{
		User:   "admin",
		Passwd: "admin@123",
	}

	// 创建连接
	conn, err := grpc.Dial(":8800", grpc.WithTransportCredentials(creds), grpc.WithPerRPCCredentials(token))
	if err != nil {
		log.Fatal("服务端连接失败: ", err)
	}

	fmt.Println("证书认证通过")

	// 退出时关闭连接
	defer conn.Close()

	// 创建客户端实例
	productServiceClient := service.NewProdServiceClient(conn)

	// 方法请求
	resq, err := productServiceClient.GetProductStock(context.Background(), &service.ProductRequest{ProdId: 233})
	if err != nil {
		log.Fatal("调用gRPC方法失败: ", err)
	}

	fmt.Println("调用gRPC方法成功, ProdStock = ", resq.ProdStock)
}

测试

1)先启动服务端,再启动服务端

服务端

在这里插入图片描述

客户端

在这里插入图片描述

token认证通过。

工程框架

在这里插入图片描述

部分参考:

HTTPS协议:https://blog.csdn.net/qq_34827674/article/details/112589634

数字签名和数字证书:https://blog.csdn.net/qq_34827674/article/details/119081396

单向认证与双向认证:https://blog.csdn.net/qq_53267860/article/details/125045094

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
gRPC是一种高性能、开源的远程过程调用(RPC)框架,它使用Protocol Buffers作为接口定义语言(IDL)。双向认证是指在进行gRPC通信时,服务端和客户端都需要进行身份验证。在gRPC中,双向认证可以通过使用TLS/SSL证书来实现。 在gRPC中实现双向认证需要以下步骤: 1. 生成证书:首先,需要生成服务端和客户端的证书。可以使用openssl或其他工具来生成证书和私钥。 2. 配置TLS/SSL:服务端和客户端都需要配置TLS/SSL来启用加密通信。服务端需要加载证书和私钥,而客户端需要加载服务端的证书用于验证服务端身份。 3. 配置双向认证:服务端和客户端都需要配置双向认证。服务端需要验证客户端的证书,而客户端需要验证服务端的证书。 4. 实现认证逻辑:在服务端和客户端的代码中,需要实现证书验证逻辑。可以使用TLS配置中的回调函数来进行验证。 可以参考引用\[2\]中提供的gRPC-Gateway和引用\[3\]中的依赖安装命令来实现gRPC双向认证。这些工具和库可以帮助简化双向认证的实现过程。 总结起来,golang中实现gRPC双向认证的步骤包括生成证书、配置TLS/SSL、配置双向认证和实现认证逻辑。通过这些步骤,可以确保服务端和客户端之间的通信是安全和可信的。 #### 引用[.reference_title] - *1* [Go Grpc Jwt身份认证和Gateway集成以及HTTPS双向认证](https://blog.csdn.net/dz45693/article/details/112180692)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [【gRPC】双向认证grpc-gateway原理及简单使用](https://blog.csdn.net/dl962454/article/details/124384299)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值