【gRPC】双向认证下grpc-gateway原理及简单使用

【gRPC】双向认证下grpc-gateway原理及简单使用,第1张

文章目录
    • 前言
    • 一、grpc-gateway原理
    • 二、 环境准备
    • 三、服务端改造
    • 四、总结

前言

在上一篇文章自签CA、服务端和客户端双向认证中,我们了解了双向认证并进行了实践,本篇文章将基于双向认证,使用gRPC-Gateway提供http请求处理的api,这样便于提供gRPC和RESTful风格的API。官方地址:https://github.com/grpc-ecosystem/grpc-gateway,本篇文章中的代码上传在gitee上:tonghua / grpc-gateway

一、grpc-gateway原理

gRPC-Gateway是Protocol Buffers编译器协议的一个插件。它读取Protobuf服务定义并生成一个反向代理服务器,该服务器将RESTful HTTP API转换为gRPC。这种服务是根据google.api.http annotations注解生成的。具体而言,当客户端发送http请求时候,grpc-gateway接受请求,生成grpc的client去请求grpc的server端;grpc-gateway实际上做了反向代理的作用。因此会产生两个服务,一个是grpc-gateway产生的http服务,负责接受客户端的http请求,一个grpc的server服务,负责处理client端的请求。

文档地址:https://grpc-ecosystem.github.io/grpc-gateway/,可以简单理解grpc-gateway就是在grpc的基础之上加一层代理并转发,转变成protobuf格式来访问grpc服务。

二、 环境准备

新版本的中对环境的一些依赖做了调整,因此我们需要安装下面的依赖:

go get -u github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway 
go get -u github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 
go get -u google.golang.org/protobuf/cmd/protoc-gen-go 
go get -u google.golang.org/grpc/cmd/protoc-gen-go-grpc

安装完成之后在自己的GOPATH下能找到下面几个可执行文件:

三、服务端改造

接下来为了使用google/api/annotations.proto注解中的内容,我们在External Libraries将grpc-gateway包中google包下的内容全部拷贝到项目的目录下,同时最好打开几个Proto文件看看包导入的问题,发现爆红直接删除掉import,ProtoBuf插件能进行自动的包导入:


为了便于后面的描述,我们可以看下目录结构:

然后我们对Prod.proto文件进行改写,使其中的ProdService能够提供http的服务。

syntax = "proto3"; //proto3的语法,不写会默认为proto2
package services; //包名,通过protoc生成go文件时使用

option go_package = "../services"; //添加生成go文件的路径

import "google/api/annotations.proto";

message ProdRequest {
  int32 prod_id = 1; //传入的商品id
}
message ProdResponse {
  int32 prod_stock =1; //商品库存
}
service ProdService {
  rpc GetProdStock (ProdRequest) returns (ProdResponse){
    option (google.api.http) = {
      get: "/v1/prod/{prod_id}" //注意这里的路径参数要和上面ProdRequest中定义的保持一致
    };
  }
}

再利用下面的两个命令生成相关的go文件,执行下面命令时需要在Prod.proto的目录下执行。生成的go文件都在services目录下:

protoc --go_out=plugins=grpc:../services Prod.proto //生成Prod.pb.go
protoc --grpc-gateway_out=logtostderr=true:../services Prod.proto //生成Prod.pb.gw.go

然后将客户端的证书和密钥拷贝到服务端的keys目录下
改写服务端的server.go代码,抽取其中的证书认证代码到helper/helper.go中,同时将客户端的证书认证代码也放到这儿:

helper.go:

package helper

import (
	"crypto/tls"
	"crypto/x509"
	"google.golang.org/grpc/credentials"
	"io/ioutil"
)

func GetServeCreds() credentials.TransportCredentials {
	// TLS认证
	//从证书相关文件中读取和解析信息,得到证书公钥、密钥对
	cert, _ := tls.LoadX509KeyPair("keys/server.pem", "keys/server.key")
	certPool := x509.NewCertPool() //初始化一个CertPool
	ca, _ := ioutil.ReadFile("keys/ca.pem")
	certPool.AppendCertsFromPEM(ca) //解析传入的证书,解析成功会将其加到池子中
	creds := credentials.NewTLS(&tls.Config{ //构建基于TLS的TransportCredentials选项
		Certificates: []tls.Certificate{cert}, //服务端证书链,可以有多个
		ClientAuth:   tls.RequireAndVerifyClientCert, //要求必须验证客户端证书
		ClientCAs:    certPool, //设置根证书的集合,校验方式使用 ClientAuth 中设定的模式
	})
	return creds
}

func GetClientCreds() credentials.TransportCredentials{
	// TLS连接
	//从证书相关文件中读取和解析信息,得到证书公钥、密钥对
	cert, _ := tls.LoadX509KeyPair("keys/client.pem", "keys/client.key")
	certPool := x509.NewCertPool()
	ca, _ := ioutil.ReadFile("keys/ca.pem")
	certPool.AppendCertsFromPEM(ca)

	creds := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{cert}, //客户端证书
		ServerName:   "www.p-pp.cn", //注意这里的参数为配置文件中所允许的ServerName,也就是其中配置的DNS...
		RootCAs:      certPool,
	})
	return creds
}

servier.go

package main

import (
	"google.golang.org/grpc"
	"grpcpro/helper"
	"grpcpro/services"
	"log"
	"net"
)
const (
	// Address gRPC服务地址
	Address = "127.0.0.1:8888"
)
func main() {
	rpcServer := grpc.NewServer(grpc.Creds(helper.GetServeCreds())) //实例化grpc Server
	//创建带ca证书验证的服务端
	services.RegisterProdServiceServer(rpcServer,new(services.ProdService))
	listen, _ := net.Listen("tcp",Address) //设置传输协议和监听地址
	log.Println("Listen on " + Address + " with TLS")
	rpcServer.Serve(listen)
}

接下来我们编写httpServer.go用于接收客户端的http请求和转发代理该请求:

package main

import (
	"context"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"google.golang.org/grpc"
	"grpcpro/helper"
	"grpcpro/services"
	"log"
	"net/http"
)
func main() {
	gwmux := runtime.NewServeMux() //创建路由
	opt := []grpc.DialOption{grpc.WithTransportCredentials(helper.GetClientCreds())}
	err := services.RegisterProdServiceHandlerFromEndpoint(context.Background(),gwmux,"127.0.0.1:8888",opt) //第二个参数为gRPC服务的地址
	if err != nil {
		log.Fatal(err)
	}
	httpServer := &http.Server{
		Addr: "127.0.0.1:8080", //客户端请求的http地址
		Handler: gwmux,
	}
	httpServer.ListenAndServe()
}

最后,先启动server.go,再启动httpServer.go,访问http://localhost:8080/v1/prod/21```即可得到gRPC提供服务的结果:

四、总结

本篇文章是在上一篇文章的双向认证的基础之上完成的,通过protobuf的自定义option实现了一个网关,在服务端同时开启gRPC和HTTP服务,HTTP服务接收客户端请求后转换为grpc请求数据,获取响应后转为json数据返回给客户端。

提示:现在go相关的版本迭代更新比较快,视频教程或一些博文都是老版本的,给我们这种初学者带来负担,但也要不停尝试,依赖这种东西尽量以官方的为准,多看看官方文档,不停试坑。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/735484.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-27
下一篇 2022-04-27

发表评论

登录后才能评论

评论列表(0条)

保存