当前位置: 首页 > news >正文

golang工程——grpc-gateway 转发http header中自定义字段到grpc上下文元数据

http header 转发到 grpc上下文

grpc网关可以将请求体内容转发到grpc对应消息中。那如何获取http header头中的信息,本文将介绍如何将http header转发到grpc上下文并采用拦截器,获取http header中的内容。 有些http header中的内置字段是会转发的比如Authorization,但是狠多自定义字段是转发不了的。

本文实现http header中自定义字段转发到grpc上下文并采用拦截器做个简单鉴权

代码可以参考前面几篇grpc-gateway博客

grpc-gateway入门,环境+简单案例

grpc-gateway proto定义http路由

grpc-gateway定义http路由

网关代码修改

如果要转发http header中的自定义内容,生成的网关代码需要进行修改,增加一些网关服务器选项

  • runtime.WithIncomingHeaderMatcher: 请求http header 设置转发哪些到grpc上下文
  • runtime.WithOutgoingHeaderMatcher: 响应后,grpc上下文转发到http头部

gateway.go

package gatewayimport ("context""flag""fmt""net/http""github.com/grpc-ecosystem/grpc-gateway/v2/runtime""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure"_ "google.golang.org/grpc/grpclog"gw "user/proto"  // Update
)var (// command-line options:// gRPC server endpointgrpcServerEndpoint = flag.String("grpc-server-endpoint",  "localhost:50051", "gRPC server endpoint")
)func Run() error {ctx := context.Background()ctx, cancel := context.WithCancel(ctx)defer cancel()// 请求时,将http header中某些字段转发到grpc上下文inComingOpt :=  runtime.WithIncomingHeaderMatcher(func(s string) (string, bool) {fmt.Println("header:" + s)switch s {case "Service-Authorization":fmt.Println("Service-Authorization hit")return "Service-Authorization", truedefault:return "", false}})// 响应后,grpc上下文转发到http头部outGoingOpt := runtime.WithOutgoingHeaderMatcher(func(s string) (string, bool) {return "", false})// Register gRPC server endpoint// Note: Make sure the gRPC server is running properly and accessiblemux := runtime.NewServeMux(inComingOpt, outGoingOpt)//添加文件上传处理函数mux.HandlePath("POST", "/upload", uploadHandler)opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}err := gw.RegisterUserHandlerFromEndpoint(ctx, mux,  *grpcServerEndpoint, opts)if err != nil {return err}// Start HTTP server (and proxy calls to gRPC server endpoint)return http.ListenAndServe(":8081", mux)
}

文件上传接口修改,因为这是自定义的网关路由接口,需要自己将Header中的字段转发到grpc中

upload.go

package gatewayimport ("context""fmt""github.com/golang/protobuf/jsonpb""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure""google.golang.org/grpc/metadata""io""net/http""user/proto"
)func uploadHandler(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {// 先从request解析文件err := r.ParseForm()if err != nil {http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)}f, header, err :=r.FormFile("attachment")if err != nil {http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)}defer f.Close()// 访问grpc server端, 实际生产用连接池conn, err := grpc.Dial(*grpcServerEndpoint, grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)}defer conn.Close()c := proto.NewUserClient(conn)ctx := context.Background()ctx = metadata.NewOutgoingContext(ctx, metadata.New(map[string]string{"file_name":header.Filename,"service-authorization":r.Header.Get("Service-Authorization"),}))stream, err := c.Upload(ctx)if err != nil {http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)}// 读文件流 转发给grpcbuf := make([]byte, 512)for {n, err := f.Read(buf)if err != nil && err != io.EOF{http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)}if n == 0 {break}stream.Send(&proto.UploadRequest{Content: buf[:n],Size: int64(n),})}res, err := stream.CloseAndRecv()if err != nil {http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)}m := jsonpb.Marshaler{}str, _ := m.MarshalToString(res)if err != nil {http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)}w.Header().Add("Content-Type", "application/json")fmt.Fprintf(w, str)}
grpc服务代码修改

拦截器,从上下文中获取元数据进行业务操作即可

interceptor.go

package serverimport ("context""errors""fmt""google.golang.org/grpc""google.golang.org/grpc/metadata""strings"
)func UnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {err = auth(ctx)if err != nil {return nil, err}return handler(ctx, req)
}func StreamInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {err := auth(ss.Context())if err != nil {return err}return handler(srv, ss)
}func auth(ctx context.Context) error {md, ok := metadata.FromIncomingContext(ctx)fmt.Println("meta:", md)// 实际应用中,返回前端提示需模糊化,详细错误可以打印日志if !ok {return errors.New("获取元数据失败,身份校验失败")}// 转发过来都是小写authorization := md["service-authorization"]if len(authorization) < 1 {return errors.New("获取身份令牌失败,身份校验失败")}token := strings.TrimPrefix(authorization[0], "Bearer ")if token != bearerToken {return errors.New("身份令牌对比失败,身份校验失败")}return nil
}// 测试用
var bearerToken = "sdfdlsdhgeiasdxzasqqqy2ybfhhu2gyvb"

并将拦截器注册到grpc服务中

s := grpc.NewServer(grpc.UnaryInterceptor(server.UnaryInterceptor), grpc.StreamInterceptor(server.StreamInterceptor))

重点还是网关代码修改,增加转发header的逻辑

http://www.lryc.cn/news/209822.html

相关文章:

  • CPU眼里的C/C++: 1.3 汇编级单步调试函数执行过程
  • 数据结构时间复杂度(补充)和空间复杂度
  • Mac-postman存储文件目录
  • JAVA面试题简单整理
  • dd命令用法学习,是一个功能强大的工具
  • Games104现代游戏引擎笔记 网络游戏进阶架构
  • Apollo 快速上手指南:打造自动驾驶解决方案
  • C现代方法(第14章)笔记——预处理器
  • Kafka KRaft模式探索
  • LVS-keepalived实现高可用
  • Linux内核驱动开发的需要掌握的知识点
  • nginx 动静分离 防盗链
  • MYSQL(索引篇)
  • Java API访问HDFS
  • 高三高考免费试卷真题押题知识点合集
  • css 计算函数属性:calc() 不起效 原因
  • 2、TB6600驱动器介绍【51单片机控制步进电机-TB6600系列】
  • Vue3:将表格数据下载为excel文件
  • vue+Fullcalendar
  • Spring定时任务+webSocket实现定时给指定用户发送消息
  • C语言学习笔记(六):数组(1)
  • apk反编译修改教程系列-----修改apk中的图片 任意更换apk桌面图片【三】
  • 【IO面试题 五】、 Serializable接口为什么需要定义serialVersionUID变量?
  • san.js源码解读之模版解析(parseTemplate)篇——readIdent函数
  • 【excel技巧】excel单元格内如何换行?
  • SSD1306 oled显示屏的驱动SPI接口
  • RSA:基于小加密指数的攻击方式与思维技巧
  • Vuex 和 Redux 的区别?
  • 软考高级系统架构师冲关预测
  • 华为实验基础(1):交换机基础