精华内容
下载资源
问答
  • Golang Gin grpc服务

    千次阅读 2019-02-21 16:40:09
    grpc/ ├── gin │ └── main.go ├── grpc │ └── server.go ├── pb │ ├── helloworld.pb.go │ └── helloworld.proto └── README.md gin main.go package main import ( "...

    代码目录

    grpc/
    ├── gin
    │   └── main.go
    ├── grpc
    │   └── server.go
    ├── pb
    │   ├── helloworld.pb.go
    │   └── helloworld.proto
    └── README.md
    

    gin

    main.go

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    
    	"github.com/gin-gonic/gin"
    	pb "github.com/gin-gonic/gin/examples/grpc/pb"
    	"google.golang.org/grpc"
    )
    
    func main() {
    	// Set up a connection to the server.
    	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
    	if err != nil {
    		log.Fatalf("did not connect: %v", err)
    	}
    	defer conn.Close()
    	client := pb.NewGreeterClient(conn)
    
    	// Set up a http server.
    	r := gin.Default()
    	r.GET("/rest/n/:name", func(c *gin.Context) {
    		name := c.Param("name")
    
    		// Contact the server and print out its response.
    		req := &pb.HelloRequest{Name: name}
    		res, err := client.SayHello(c, req)
    		if err != nil {
    			c.JSON(http.StatusInternalServerError, gin.H{
    				"error": err.Error(),
    			})
    			return
    		}
    
    		c.JSON(http.StatusOK, gin.H{
    			"result": fmt.Sprint(res.Message),
    		})
    	})
    
    	// Run http server
    	if err := r.Run(":8052"); err != nil {
    		log.Fatalf("could not run server: %v", err)
    	}
    }
    
    

    grpc

    server.go

    package main
    
    import (
    	"log"
    	"net"
    
    	pb "github.com/gin-gonic/gin/examples/grpc/pb"
    	"golang.org/x/net/context"
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/reflection"
    )
    
    // server is used to implement helloworld.GreeterServer.
    type server struct{}
    
    // SayHello implements helloworld.GreeterServer
    func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    	return &pb.HelloReply{Message: "Hello " + in.Name}, nil
    }
    
    func main() {
    	lis, err := net.Listen("tcp", ":50051")
    	if err != nil {
    		log.Fatalf("failed to listen: %v", err)
    	}
    	s := grpc.NewServer()
    	pb.RegisterGreeterServer(s, &server{})
    
    	// Register reflection service on gRPC server.
    	reflection.Register(s)
    	if err := s.Serve(lis); err != nil {
    		log.Fatalf("failed to serve: %v", err)
    	}
    }
    
    

    pb

    helloworld.proto

    syntax = "proto3";
    
    option java_multiple_files = true;
    option java_package = "io.grpc.examples.helloworld";
    option java_outer_classname = "HelloWorldProto";
    
    package helloworld;
    
    // The greeting service definition.
    service Greeter {
      // Sends a greeting
      rpc SayHello (HelloRequest) returns (HelloReply) {}
    }
    
    // The request message containing the user's name.
    message HelloRequest {
      string name = 1;
    }
    
    // The response message containing the greetings
    message HelloReply {
      string message = 1;
    }
    

    helloworld.pb.go

    // Code generated by protoc-gen-go.
    // source: helloworld.proto
    // DO NOT EDIT!
    
    /*
    Package helloworld is a generated protocol buffer package.
    
    It is generated from these files:
    	helloworld.proto
    
    It has these top-level messages:
    	HelloRequest
    	HelloReply
    */
    package helloworld
    
    import proto "github.com/golang/protobuf/proto"
    import fmt "fmt"
    import math "math"
    
    import (
    	context "golang.org/x/net/context"
    	grpc "google.golang.org/grpc"
    )
    
    // Reference imports to suppress errors if they are not otherwise used.
    var _ = proto.Marshal
    var _ = fmt.Errorf
    var _ = math.Inf
    
    // This is a compile-time assertion to ensure that this generated file
    // is compatible with the proto package it is being compiled against.
    // A compilation error at this line likely means your copy of the
    // proto package needs to be updated.
    const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
    
    // The request message containing the user's name.
    type HelloRequest struct {
    	Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
    }
    
    func (m *HelloRequest) Reset()                    { *m = HelloRequest{} }
    func (m *HelloRequest) String() string            { return proto.CompactTextString(m) }
    func (*HelloRequest) ProtoMessage()               {}
    func (*HelloRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
    
    // The response message containing the greetings
    type HelloReply struct {
    	Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"`
    }
    
    func (m *HelloReply) Reset()                    { *m = HelloReply{} }
    func (m *HelloReply) String() string            { return proto.CompactTextString(m) }
    func (*HelloReply) ProtoMessage()               {}
    func (*HelloReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
    
    func init() {
    	proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest")
    	proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply")
    }
    
    // Reference imports to suppress errors if they are not otherwise used.
    var _ context.Context
    var _ grpc.ClientConn
    
    // This is a compile-time assertion to ensure that this generated file
    // is compatible with the grpc package it is being compiled against.
    const _ = grpc.SupportPackageIsVersion4
    
    // Client API for Greeter service
    
    type GreeterClient interface {
    	// Sends a greeting
    	SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
    }
    
    type greeterClient struct {
    	cc *grpc.ClientConn
    }
    
    func NewGreeterClient(cc *grpc.ClientConn) GreeterClient {
    	return &greeterClient{cc}
    }
    
    func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
    	out := new(HelloReply)
    	err := grpc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, c.cc, opts...)
    	if err != nil {
    		return nil, err
    	}
    	return out, nil
    }
    
    // Server API for Greeter service
    
    type GreeterServer interface {
    	// Sends a greeting
    	SayHello(context.Context, *HelloRequest) (*HelloReply, error)
    }
    
    func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
    	s.RegisterService(&_Greeter_serviceDesc, srv)
    }
    
    func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
    	in := new(HelloRequest)
    	if err := dec(in); err != nil {
    		return nil, err
    	}
    	if interceptor == nil {
    		return srv.(GreeterServer).SayHello(ctx, in)
    	}
    	info := &grpc.UnaryServerInfo{
    		Server:     srv,
    		FullMethod: "/helloworld.Greeter/SayHello",
    	}
    	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
    		return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
    	}
    	return interceptor(ctx, in, info, handler)
    }
    
    var _Greeter_serviceDesc = grpc.ServiceDesc{
    	ServiceName: "helloworld.Greeter",
    	HandlerType: (*GreeterServer)(nil),
    	Methods: []grpc.MethodDesc{
    		{
    			MethodName: "SayHello",
    			Handler:    _Greeter_SayHello_Handler,
    		},
    	},
    	Streams:  []grpc.StreamDesc{},
    	Metadata: "helloworld.proto",
    }
    
    func init() { proto.RegisterFile("helloworld.proto", fileDescriptor0) }
    
    var fileDescriptor0 = []byte{
    	// 174 bytes of a gzipped FileDescriptorProto
    	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9,
    	0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0x88,
    	0x28, 0x29, 0x71, 0xf1, 0x78, 0x80, 0x78, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42,
    	0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, 0x92,
    	0x1a, 0x17, 0x17, 0x54, 0x4d, 0x41, 0x4e, 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x6e, 0x6a, 0x71, 0x71,
    	0x62, 0x3a, 0x4c, 0x11, 0x8c, 0x6b, 0xe4, 0xc9, 0xc5, 0xee, 0x5e, 0x94, 0x9a, 0x5a, 0x92, 0x5a,
    	0x24, 0x64, 0xc7, 0xc5, 0x11, 0x9c, 0x58, 0x09, 0xd6, 0x25, 0x24, 0xa1, 0x87, 0xe4, 0x02, 0x64,
    	0xcb, 0xa4, 0xc4, 0xb0, 0xc8, 0x00, 0xad, 0x50, 0x62, 0x70, 0x32, 0xe0, 0x92, 0xce, 0xcc, 0xd7,
    	0x4b, 0x2f, 0x2a, 0x48, 0xd6, 0x4b, 0xad, 0x48, 0xcc, 0x2d, 0xc8, 0x49, 0x2d, 0x46, 0x52, 0xeb,
    	0xc4, 0x0f, 0x56, 0x1c, 0x0e, 0x62, 0x07, 0x80, 0xbc, 0x14, 0xc0, 0x98, 0xc4, 0x06, 0xf6, 0x9b,
    	0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x0f, 0xb7, 0xcd, 0xf2, 0xef, 0x00, 0x00, 0x00,
    }
    
    

    How to run this example

    1. run grpc server
    $ go run grpc/server.go
    
    1. run gin server
    $ go run gin/main.go
    
    1. use curl command to test it
    $ curl -v 'http://localhost:8052/rest/n/thinkerou'
    
    展开全文
  • [go] gin+grpc

    千次阅读 2019-07-31 17:47:50
    参考: http://doc.oschina.net/grpc?t=60133 https://github.com/grpc/grpc-go 安装protobuf 1.brew install protobuf ...安装grpc ...1.go get google.golang.org/grpc 2.go get github.com/...

    参考:

    http://doc.oschina.net/grpc?t=60133

    https://github.com/grpc/grpc-go

    安装protobuf

    1.brew install protobuf

    2.protoc --version

     

    安装grpc

    1.go get google.golang.org/grpc

    2.go get github.com/golang/protobuf/protoc-gen-go

     

    生成 pb.go

    1.protoc --go_out=plugins=grpc:. ServeRoute.proto

     

    安装gin

    1.go get github.com/gin-gonic/gin

     

    测试运行

    1.serve+client

    2.serve+main

     

    访问localhost:9090/grpc

     

    设置proxy

    export GO111MODULE=on

    export GOPROXY=https://goproxy.io

     

    代码:

    https://github.com/zld126126/grpc-test

    展开全文
  • 在基于go的web服务中,常常需要进行用户的权限验证,一般会使用JWT,但是在gin中我们也可以通过自定义中间件来实现用户的权限验证。 目录 1.基于gin中间件的用户验证 2.结合casbin的gin的中间件的用户权限验证 ...

    在基于go的web服务中,常常需要进行用户的权限验证,一般会使用JWT,但是在gin中我们也可以通过自定义中间件来实现用户的权限验证,在gRPC中可以通过拦截器来实现。

    目录

    1.基于gin中间件的用户验证

    2.结合casbin的gin的中间件的用户权限验证

    (1) 建立conf文件

    (2) 建立用户权限列表csv文件

    (3) 使用gin中间件进行权限验证

    (4) 测试

    (5) 扩展

    3.gRPC中的Token认证

    (1)gRPC中的拦截器

    (2)UnaryServerInterceptor的使用

    (3)StreamServerInterceptor的使用

    (4)在服务中注册拦截器

    4.总结

     

    1.基于gin中间件的用户验证

    有些时候我们可能只需要简单的用户认证,例如restful接口,当用户登陆之后后台返回给调用者一个token,之后所有的请求头部都会携带上这个token,然后后端拿到请求的数据,如果请求头部中的token验证通过,那么就认为具有相应的权限。这种情况我们可以通过自定义gin中的中间件来实现。

    package main
    
    import (
    	"github.com/casbin/casbin/v2"
    	"github.com/gin-gonic/gin"
    	"log"
    	"net/http"
    )
    
    func main() {
    	r := gin.Default()
    	r.Use(authorizationWithCasbin())
    	r.GET("/test", func(c *gin.Context) {
    		 c.JSON(http.StatusOK, gin.H{"user": "admin", "secret": "admin@123"})
    	})
    	_ = r.Run(":8080")
    }
    
    // 自定义的用户验证的中间件
    func authorization() gin.HandlerFunc {
    	return func(c *gin.Context) {
    		if userName, passWord, ok := c.Request.BasicAuth();!ok{
    			c.AbortWithStatus(401)
    		}else{
    			if userName != "seu" || passWord != "admin@123"{
    				c.AbortWithStatus(401)
    			}
    		}
    	}
    }

    但是需要注意的的是c.Request.BasicAuth的使用,我们可以看一下它的源代码如下:

    // BasicAuth returns the username and password provided in the request's
    // Authorization header, if the request uses HTTP Basic Authentication.
    // See RFC 2617, Section 2.
    func (r *Request) BasicAuth() (username, password string, ok bool) {
    	auth := r.Header.Get("Authorization")
    	if auth == "" {
    		return
    	}
    	return parseBasicAuth(auth)
    }
    
    // parseBasicAuth parses an HTTP Basic Authentication string.
    // "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true).
    func parseBasicAuth(auth string) (username, password string, ok bool) {
    	const prefix = "Basic "
    	// Case insensitive prefix match. See Issue 22736.
    	if len(auth) < len(prefix) || !strings.EqualFold(auth[:len(prefix)], prefix) {
    		return
    	}
    	c, err := base64.StdEncoding.DecodeString(auth[len(prefix):])
    	if err != nil {
    		return
    	}
    	cs := string(c)
    	s := strings.IndexByte(cs, ':')
    	if s < 0 {
    		return
    	}
    	return cs[:s], cs[s+1:], true
    }

    可以看到它是先获取请求头部中的Authorization字段,然后再去解析,并且该字段的值必须是以Basic 开头,后面是对应的userName:userInfo的base64编码形式。例如,我们的用户名是seu,密码是admin@123,首先我们获取seu:admin@123的base64编码为c2V1OmFkbWluQDEyMw==(这里为了方便起见我直接将返回的token用用户的密码进行替代),最终请求头部的Authorization字段就是Basic c2V1OmFkbWluQDEyMw==。我们可以使用Goland中的HttpClient发送Get请求进行验证

    GET http://localhost:8080/test
    Accept: application/json
    Authorization: Basic c2V1OmFkbWluQDEyMw==
    输出:
    # {
      "secret": "admin@123",
      "user": "admin"
    }
    
    GET http://localhost:8080/test
    Accept: application/json
    Authorization: Basic c2V1OmFkbWluQDEyMw===
    输出:
    # Response code: 401 (Unauthorized); Time: 96ms; Content length: 0 bytes
    

    2.结合casbin的gin的中间件的用户权限验证

    有些时候我们需要更复杂的权限验证,比如特定的用户只能请求某些特定的restful接口,这个时候我们可以使用casbin来帮助我们进行权限的验证。关于casbin大家可以参考以下官网文档和相关文章

    Github: https://github.com/casbin/casbin

    Document: https://casbin.org/docs/en/overview

    相关文章: https://cloud.tencent.com/developer/article/1534674

    概括的说就是在使用casbin之前我们需要先建立一个model的控制文件,以及对应的用户权限列表,用户权限列表可以存在mysql中或者是csv中,然后还是和上面一样,根据请求头部中的Authorization字段去获取用户名,然后根据用户名去拉取用户权限,再使用casbin进行权限的比对来判断该用户是否具有相关的权限。

    例如现在有2个用户Tom和Bob,其中Tom只能以GET的方式访问/testTom这个接口,Bob只能以POST的方式访问/testBob这个接口,我们可以通过以下的方式来实现这样要求

    (1) 建立conf文件

    [request_definition]
    r = sub, obj, act
    
    [policy_definition]
    p = sub, obj, act
    
    [policy_effect]
    e = some(where (p.eft == allow))
    
    [matchers]
    m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

     其中request_definition中定义的是请求的字段,sub,obj,act可以理解成分别代表用户的名称,用户要操作的对象和用户要进行的操作。相对应的policy_definition中即为对应的用户权限列表。

    (2) 建立用户权限列表csv文件

    p,Tom, /testTom, GET
    p,Bob, /testBob, POST

    (3) 使用gin中间件进行权限验证

    package main
    
    import (
    	"github.com/casbin/casbin/v2"
    	"github.com/gin-gonic/gin"
    	"log"
    	"net/http"
    )
    
    func main() {
    	r := gin.Default()
    	r.Use(authorizationWithCasbin())
    	r.GET("/testTom", func(c *gin.Context) {
    		 c.JSON(http.StatusOK, gin.H{"user": "Tom"})
    	})
    	r.POST("/testBob", func(c *gin.Context) {
    		c.JSON(500, gin.H{"user": "Bob"})
    	})
    	_ = r.Run(":8080")
    }
    
    
    func authorizationWithCasbin() gin.HandlerFunc {
    	return func(c *gin.Context) {
    		if userName, passWord, ok := c.Request.BasicAuth();!ok{
    			c.AbortWithStatus(401)
    		}else{
    			// 这里可以根据用户名称去拉取密码和用户信息进行进一步的验证
    			if passWord == "admin@123" {
    				e, err := casbin.NewSyncedEnforcer("model.conf", "user.csv")
    				if err != nil{
    					log.Print(err)
    				}
    				sub := userName
    				obj := c.Request.URL.String()
    				act := c.Request.Method
    				if ok, _ := e.Enforce(sub, obj, act);!ok{
    					c.AbortWithStatus(401)
    				}
    			}else{
    				c.AbortWithStatus(401)
    			}
    		}
    	}
    }

    我们首先获取对应的信息,然后使用casbin进行验证即可

    (4) 测试

    我们还是使用Goland自带的HttpClient对其进行验证

    (1)Tom:admin@123
    GET http://localhost:8080/testTom
    Accept: application/json
    Authorization: Basic VG9tOmFkbWluQDEyMw==
    >>> {"user": "Tom"}
    
    (2)Bob:admin@123
    GET http://localhost:8080/testTom
    Accept: application/json
    Authorization: Basic Qm9iOmFkbWluQDEyMw==
    >>> Response code: 401 (Unauthorized); Time: 96ms; Content length: 0 bytes
    
    (3)Tom:admin@123
    POST http://localhost:8080/testBob
    Accept: application/json
    Authorization: Basic VG9tOmFkbWluQDEyMw==
    >>> Response code: 401 (Unauthorized); Time: 93ms; Content length: 0 bytes
    
    (4)
    POST http://localhost:8080/testBob
    Accept: application/json
    Authorization: Basic VG9tOmFkbWluQDEyMw==
    >>> >>> {"user": "Bob"}

    (5) 扩展

    使用casbin我们可以很方便的进行权限修改与扩展,例如我们现在想要所有的用户都可以访问/testTom这个接口,对应的我们只需要修改conf文件和csv文件如下:

    [request_definition]
    r = sub, obj, act
    
    [policy_definition]
    p = sub, obj, act
    
    [policy_effect]
    e = some(where (p.eft == allow))
    
    [matchers]
    m = (r.sub == p.sub && r.obj == p.obj && r.act == p.act) || p.sub == "*"
    p,*, /testTom, GET
    p,Bob, /testBob, POST

    3.gRPC中的Token认证

    上面介绍的是如何在gin中进行token认证,如果使用的是gRPC,同样的我们可以使用官方提供的拦截器很容易的实现token认证,详情可以参考官方文档:https://pkg.go.dev/google.golang.org/grpc#UnaryClientInterceptor,接下来就以我自己的项目GDB中的例子来说明如何使用拦截器使用token认证。详细的代码可以参考github: https://github.com/JustKeepSilence/gdb

    (1)gRPC中的拦截器

    在gRPC中服务端有两种拦截器,针对一般函数的拦截器UnaryServerInterceptor和针对流函数的StreamServerInterceptor拦截器,它们的定义分别如下

    # UnaryServerInterceptor 
    type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)
    
    # StreamServerInterceptor 
    type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error

    其中,UnaryServerInterceptor的参数分别为client传进来的context,client的请求req,服务端的信息info和用来处理请求的handler。StreamServerInterceptor的参数分别为客户端的请求参数srv,服务端的流信息ss和info以及用来处理请求的handler。

    (2)UnaryServerInterceptor的使用

    基本逻辑还是和前述一样,在客户端登陆的时候,首先将用户信息写入context中,然后在服务端获取用户信息并进行验证,如果验证通过则返回token信息,以后客户端的每次请求都必须携带这个token信息,我们也可以基于这个token进行更加详细的权限控制。

    下面是服务端使用UnaryServerInterceptor进行权限验证的代码

    func (s *server) authInterceptor(c context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
    	if s.configs.Authorization {
    		methods := strings.Split(info.FullMethod, "/")
    		if md, ok := metadata.FromIncomingContext(c); !ok {
    			return nil, status.Errorf(codes.Unauthenticated, "invalid token")
    		} else {
    			var userName string
    			if d, ok := md["userName"]; ok {
    				userName = d[0]
    			} else {
    				return nil, status.Errorf(codes.Unauthenticated, "invalid token")
    			}
    			remoteAddress := md.Get(":authority")[0] // address
    			userAgent := md.Get("user-agent")[0]     // user agent
    			if methods[len(methods)-1] == "UserLogin" {
    				r := req.(*pb.AuthInfo)
    				if result, err := s.gdb.userLogin(authInfo{
    					UserName: r.GetUserName(),
    					PassWord: r.GetPassWord(),
    				}, remoteAddress, userAgent); err != nil {
    					return nil, status.Errorf(codes.Unauthenticated, "invalid token")
    				} else {
    					return &pb.UserToken{Token: result.Token}, nil
    				}
    			} else {
    				var token string
    				if d, ok := md["token"]; ok {
    					token = d[0]
    				} else {
    					return nil, status.Errorf(codes.Unauthenticated, "invalid token")
    				}
    				if v, err := s.gdb.infoDb.Get([]byte(userName+"_token"+"_"+remoteAddress+"_"+userAgent), nil); err != nil || v == nil {
    					return nil, status.Errorf(codes.Unauthenticated, "invalid token")
    				} else {
    					if token != fmt.Sprintf("%s", v) {
    						return nil, status.Errorf(codes.Unauthenticated, "invalid token")
    					} else {
    						// log handler
    						return handler(c, req)
    					}
    				}
    			}
    		}
    	} else {
    		return handler(c, req)
    	}
    }

    其中server为对应的rpc服务的结构体。基本的逻辑为首先根据配置文件中的配置来判断用户是否要进行权限控制,如果确定进行权限控制的话,首先判断请求要客户端的方法,如果是登陆方法的话则直接获取用户信息,并进行验证,如果验证通过,则返回token信息,对于其他的请求方法,则获取token字段与数据库中的token信息进行比对

    (3)StreamServerInterceptor的使用

    StreamServerInterceptor的使用和UnaryServerInterceptor基本相同,下面是服务端的代码

    func (s *server) authWithServerStreamInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
    	if s.configs.Authorization {
    		if !info.IsClientStream {
    			return status.Errorf(codes.Unknown, "unknown service type")
    		} else {
    			if md, ok := metadata.FromIncomingContext(ss.Context()); !ok {
    				return status.Errorf(codes.Unauthenticated, "invalid token")
    			} else {
    				var userName, token string
    				remoteAddress := md.Get(":authority")[0] // address
    				userAgent := md.Get("user-agent")[0]     // user agent
    				if d, ok := md["userName"]; ok {
    					userName = d[0]
    				} else {
    					return status.Errorf(codes.Unauthenticated, "invalid token")
    				}
    				if d, ok := md["token"]; ok {
    					token = d[0]
    				} else {
    					return status.Errorf(codes.Unauthenticated, "invalid token")
    				}
    				if v, err := s.gdb.infoDb.Get([]byte(userName+"_token"+"_"+remoteAddress+"_"+userAgent), nil); err != nil || v == nil {
    					return status.Errorf(codes.Unauthenticated, "invalid token")
    				} else {
    					if token != fmt.Sprintf("%s", v) {
    						return status.Errorf(codes.Unauthenticated, "invalid token")
    					} else {
    						return handler(srv, ss)
    					}
    				}
    			}
    		}
    	} else {
    		return handler(srv, ss)
    	}
    }

    也是获取请求中的token信息并进行对应的验证

    (4)在服务中注册拦截器

    在实现完了拦截器之后,我们还需要在启动gRPC服务之前去进行注册,并且现在官方已经支持了链式注册,也就是说我们可以使用多个拦截器,而不需要像以前一样将所有的逻辑都放在一个函数中,对于UnaryServerInterceptor使用ChainUnaryInterceptor,对于StreamServerInterceptor则使用ChainStreamInterceptor,使用代码如下

    s = grpc.NewServer(grpc.ChainUnaryInterceptor(se.panicInterceptor, se.authInterceptor, se.logInterceptor),
    			grpc.ChainStreamInterceptor(se.panicWithServerStreamInterceptor, se.authWithServerStreamInterceptor))

    可以看到,我这里注册了三个拦截器,分别是panic拦截器,auth拦截器和log拦截器,并且拦截器的运行顺序和注册的顺序是一致的。

    4.总结

    在go的后台开发中,token的权限验证是很常见的问题,不管是常见的web后台还是grpc后台,我们都可以很容易实现这种需求。如果想要了解更详细的代码细节,大家可以参考我的github:https://github.com/JustKeepSilence/gdb或者与我交流~

    展开全文
  • git地址: https://github.com/zld126126/go_micro 参考: https://github.com/micro-in-cn 运行结果:
    展开全文
  • 基于自动参数绑定 doc Golang杜松子酒自动参数绑定 支持RPC自动映射 ...支持注释路由 基于json restful风格的 ...func(* gin.Context)// go-gin Raw接口 func(* api.Context)//自定义上下文类型 func(* api.Con
  • 支持grpc拦截器加签验签,对业务代码无侵入 支持gin框架中间件验签,支持客户端发送http请求设置加签信息到Header中 支持服务端对接多语言客户端(签名原文为:有序JSON(ASCII码序排序Key,忽略结构体/Map中的0值和...
  • grpc+gin+proto,使用http调用rpc接口

    千次阅读 2020-07-02 18:22:10
    grpc.WithInitialWindowSize(grpcInitialWindowSize), grpc.WithInitialConnWindowSize(grpcInitialConnWindowSize), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(grpcMaxCallMsgSize)), ...
  • P18 熔断器Hystrix 下面的go 使用Hystrix的例子 P18 Hystrix降级实现 先实现一个降级后的函数,简单处理返回结果 然后降级fallback函数调用这段降级处理逻辑,如下图 P19 整合Hystrix 到 go-......
  • 项目介绍 项目地址: : Go入门的学习笔记,从零...│ ├── 14-学习 grpc.Dial(target string, opts …DialOption) 的写法 │ ├── 15-time.RFC3339 时间格式化 │ ├── 16-常用签名算法的基准测试 │ ├─ 01-
  • P02 go gin学习 创建server后,再调用server.Run()运行 P03 服务注册到consul P05 selector 下面是RoundRobin轮询算法 然后调用方法,如下 P09 服务调用 p11 go-micro...
  • <p>We are using Improbable's <a href="https://github.com/improbable-eng/grpc-web" rel="nofollow noreferrer">gRPC-Web library</a> to expose a gRPC service (implemented in Go) to a Javascript client ...
  • Golang基础 Gin gRPC Go+Elasticsearch
  • 今天抽时间实现了nodejs的框架koa2与go的gin互相调用的功能,发现这种互相调用的方式很特别,试想一下,vue里面直接调go的函数操作数据库,不需要考虑请求,响应,跨域,并发等常规问题。同时还可以直接js调用rust,...
  • 拦截器(gRPC-Interceptor)类似于 Gin 中间件(Middleware),让你在真正调用 RPC 服务前,进行身份认证、参数校验、限流等通用操作。 系列 云原生 API 网关,gRPC-Gateway V2 初探 Go + gRPC-Gateway(V2) 构建微服务...

空空如也

空空如也

1 2 3 4 5 6
收藏数 107
精华内容 42
关键字:

gingrpc