由网络副手--寻路人于2022.11.29 08:34:00发布在Go语言 go gRpc案例分享-gRpc介绍和使用(四) 代码实现 阅读1558 评论1 喜欢0 #前言 本篇内容主要接上一篇内容继续编辑,主要针对即将使用gRpc技术的一个案例代码实现。 [go gRpc案例分享-gRpc介绍和使用(三) 认证](http://www.bravedu.com/archives/262/ "go gRpc案例分享-gRpc介绍和使用(三) 认证"~) #一、服务端代码 ``` ├── cert │ ├── ca.crt //CA 证书 │ ├── grpc.bravedu.key //服务端证书key │ └── grpc.bravedu.pem //服务端SAN证书 ├── go.mod ├── go.sum ├── grpc_server.go //微服务的启动文件 ├── pbfile //protobuf 文件存放目录 │ ├── topic.proto #topic 的定义 │ ├── user.proto │ └── user_topic_mesage.proto └── service ├── topic.pb.go //此文件为生成 ├── topic_grpc.pb.go //此文件为生成 └── topic_service.go //我们编写的Topic的Service 服务层 ``` ##1.1 定义Topic 服务 admin/pbfile/topic.proto 文件 1. 文件主要定义方法 GetTopicInfo 2. 定义Topic详情结构体 ``` //指定当前的proto 语法版本, 有v2 和 v3 两个版本,当下多数使用v3 syntax = "proto3"; //生成文件存放所在的目录,会自动生成目录 option go_package = "../service"; import "google/protobuf/timestamp.proto"; import "google/protobuf/any.proto"; //引入其他结构体, 从执行protoc 这个命令的当前目录开始算起 //指定生成出来的package 名称 package service; //消息 传输的对象 message Topic { sint32 Id = 1; //ID string Title = 2; //标题 string Content = 3; //内容 google.protobuf.Timestamp CreateAt = 4; //创建时间 } message TopicInfoRequest { sint32 Id = 1; } message TopicInfoResponse { int32 ErrorCode = 1; string ErrorMsg = 2; Topic Data = 3; } //定义服务主体 service TopicService { //定义获取话题详情 rpc GetTopicInfo(TopicInfoRequest) returns (TopicInfoResponse); } ``` ##执行生成*.pb.go 文件 ``` cd admin; mkdir -p service; //生成 protoc --go_out=./service --go-grpc_out=./service pbfile/topic.proto go mod tidy #导入依赖 ``` ##编写TopicService 业务代码Service层 ``` package service import ( "context" "google.golang.org/protobuf/types/known/timestamppb" "time" ) //导出当前服务层 var NewTopicService = &TopicService{} type TopicService struct { } //定义获取话题详情 func (t *TopicService) GetTopicInfo(ctx context.Context, req *TopicInfoRequest) (*TopicInfoResponse, error) { //TODO 这里进行数据库查询获取内容,暂时先跟进传入ID赋值吧 topicInfo := &Topic{ Id: req.Id, Title: "这里是标题", Content: "这里是话题内容", CreateAt: timestamppb.New(time.Now()), } resp := &TopicInfoResponse{ ErrorCode: 0, ErrorMsg: "成功", Data: topicInfo, } return resp, nil } //这个方法是生成的topic_grpc.pb.go 里面 type TopicServiceServer interface {} 里面的方法,为了实现接口,所以要实现空方法 func (t *TopicService) mustEmbedUnimplementedTopicServiceServer() {} ``` ## 当前服务启动文件, 证书认证 & Topic 服务代码注册 (admin/grpc_service.go) ``` package main import ( "bravedu.com/admin/service" "context" "crypto/tls" "crypto/x509" "fmt" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "io/ioutil" "log" "net" ) func main() { //验证方式采用双向认证, 采用X509认证库载入认证秘钥 cert, err := tls.LoadX509KeyPair("cert/grpc.bravedu.pem", "cert/grpc.bravedu.key") if err != nil { log.Fatalln("证书读取错误", err) } //创建一个空的CertPool certPool := x509.NewCertPool() ca, err := ioutil.ReadFile("cert/ca.crt") if err != nil { log.Fatalln("ca 证书读取错误", err) } //尝试解析所传入的PEM编码证书,如果解析成功将其添加到CertPool 中,便于后面使用 certPool.AppendCertsFromPEM(ca) //构建基于TLS的TransportCredentials选项 creds := credentials.NewTLS(&tls.Config{ //设置证书连接,允许包含一个或多个 Certificates: []tls.Certificate{cert}, //邀请必须校验客户端证书,可以根据实际情况选用以下参数 ClientAuth: tls.RequireAndVerifyClientCert, //设置根证书集合,校验方式使用ClientAuth 中设定的模式 ClientCAs: certPool, }) //是先一个拦截器,在监听启动时执行 var authInterceptor grpc.UnaryServerInterceptor authInterceptor = func( ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, ) (resp interface{}, err error) { //拦截普通方法请求,验证token err = JwtAuth(ctx) if err != nil { return } //继续处理请求 return handler(ctx, req) } rpcServer := grpc.NewServer(grpc.Creds(creds), grpc.UnaryInterceptor(authInterceptor)) //这里实现服务注册模块 service.RegisterTopicServiceServer(rpcServer, service.NewTopicService) listener, err := net.Listen("tcp", ":8003") if err != nil { log.Fatalln("启动监听出错", err) } err = rpcServer.Serve(listener) if err != nil { log.Fatalln("启动服务出错", err) } fmt.Println("启动成功") } func JwtAuth(ctx context.Context) error { //TODO 这里实现JTW认证 return nil } ``` #客户端代码 ``` ├── auth │ └── auth.go //鉴权文件 ├── cert │ ├── ca.crt │ ├── client_grpc.bravedu.key │ └── client_grpc.bravedu.pem ├── client_server.go //客户端调用文件 ├── go.mod ├── go.sum └── service ├── topic.pb.go └── topic_grpc.pb.go ``` ##1. 把服务端生成pb文件拷贝到service 里面 client |--service/topic_grpc.pb.go |--service/topic.pb.go //执行go mod tidy 导入依赖包 ##2. 编写鉴权文件 auth/auth.go ``` package auth import "context" type Authentication struct { Token string } func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { return map[string]string{"token": a.Token}, nil } //是否开启安全连接,本地就不开启了 func (a *Authentication) RequireTransportSecurity() bool { return false } ``` 文件用于服务端接口调用鉴权使用 ``` grpc.Dial(":8003", grpc.WithTransportCredentials(creds), grpc.WithPerRPCCredentials(token)) //接口调用前有认证模块,看到WithPerRPCCredentials 参数是实现了 credentials.PerRPCCredentials 接口,所以单独实现接口的两个方法 ``` ##3. 编写客户端服务,调用服务端并打印执行结果 ``` package main import ( "bravedu.com/client/auth" "bravedu.com/client/service" "context" "crypto/tls" "crypto/x509" "fmt" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "io/ioutil" "log" ) func main() { //双向认证 cert, _ := tls.LoadX509KeyPair("cert/client_grpc.bravedu.pem", "cert/client_grpc.bravedu.key") //创建一个新的、空的 CertPool certPool := x509.NewCertPool() ca, _ := ioutil.ReadFile("cert/ca.crt") // 尝试解析所有传入的pem 编码证书,如果解析成功加入到CertPool 中,便于后面使用 certPool.AppendCertsFromPEM(ca) //构建基于 TLS 的TransportCredentials 选项 creds := credentials.NewTLS(&tls.Config{ //设置证书连接、允许包含一个或者多个 Certificates: []tls.Certificate{cert}, //要求必须校验客户端的证书、可以根据实际情况选用以下参数 ServerName: "*.grpc.bravedu.com", //这里就是证书生成时候的根域名 RootCAs: certPool, }) //前置认证处理 token := &auth.Authentication{ Token: "token_abb2324542556s2323", //这里是获取的jwt token 伪代码 } conn, err := grpc.Dial(":8003", grpc.WithTransportCredentials(creds), grpc.WithPerRPCCredentials(token)) if err != nil { log.Fatalln("服务端出错,连接不上") } defer conn.Close() //上线服务代码调用, 这个方法来自于拷贝过来的pb文件中 服务端生成的内容 prodClient := service.NewTopicServiceClient(conn) request := &service.TopicInfoRequest{ Id: 1, } info, err := prodClient.GetTopicInfo(context.Background(), request) if err != nil { log.Fatalln(err) } fmt.Println(info) } ``` ##执行打印 ``` /private/var/folders/_g/yclfz8j53vb0k6qzq6q5fqsw0000gn/T/GoLand/___go_build_client_server_go ErrorMsg:"成功" Data:{Id:1 Title:"这里是标题" Content:"这里是话题内容" CreateAt:{seconds:1669728228 nanos:965350000}} ``` 赞 0 分享 赏 您可以选择一种方式赞助本站 支付宝扫码赞助 BraveDu 署名: 网络副手~寻路人
观点新颖,见解独到,发人深省。