Bring into gRPC: Make custom authentication for RPC method

  golang, grpc, php, rpc, tls

Bring into gRPC: Make custom authentication for RPC method

Original address:Bring into gRPC: Make custom authentication for RPC method
Project address:https://github.com/EDDYCJY/go …

Preface

In the previous chapter, we introduced two methods (certificate is one) that can be globally authenticated:

  1. TLS certificate authentication
  2. Ca-based TLS certificate authentication
  3. Unary and Stream interceptor

However, in actual requirements, RPC methods of some modules are often specially authenticated or verified. Today we will explain and realize the function points of this piece.

Pre-class knowledge

type PerRPCCredentials interface {
    GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
    RequireTransportSecurity() bool
}

PerRPCCredentials is defined by default in gRPC. It is the main character of this chapter. gRPC provides an interface for custom authentication by default. Its function is to add the required security authentication information to the context of each RPC method. It includes two methods:

  • GetRequestMetadata: Gets the metadata required for the currently requested authentication
  • RequireTransportSecurity: Do you need TLS authentication for secure transmission

directory structure

New simple_token_server/server.go and simple_token_client/client.go with the following directory structure:

go-grpc-example
├── client
│   ├── simple_client
│   ├── simple_http_client
│   ├── simple_token_client
│   └── stream_client
├── conf
├── pkg
├── proto
├── server
│   ├── simple_http_server
│   ├── simple_server
│   ├── simple_token_server
│   └── stream_server
└── vendor

gRPC

Client

package main

import (
    "context"
    "log"

    "google.golang.org/grpc"

    "github.com/EDDYCJY/go-grpc-example/pkg/gtls"
    pb "github.com/EDDYCJY/go-grpc-example/proto"
)

const PORT = "9004"

type Auth struct {
    AppKey    string
    AppSecret string
}

func (a *Auth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
    return map[string]string{"app_key": a.AppKey, "app_secret": a.AppSecret}, nil
}

func (a *Auth) RequireTransportSecurity() bool {
    return true
}

func main() {
    tlsClient := gtls.Client{
        ServerName: "go-grpc-example",
        CertFile:   "../../conf/server/server.pem",
    }
    c, err := tlsClient.GetTLSCredentials()
    if err != nil {
        log.Fatalf("tlsClient.GetTLSCredentials err: %v", err)
    }

    auth := Auth{
        AppKey:    "eddycjy",
        AppSecret: "20181005",
    }
    conn, err := grpc.Dial(":"+PORT, grpc.WithTransportCredentials(c), grpc.WithPerRPCCredentials(&auth))
    ...
}

On the Client side, the focus is on implementationtype PerRPCCredentials interfaceThe required method, pay attention to two points:

  • struct Auth:GetRequestMetadata、RequireTransportSecurity
  • grpc.WithPerRPCCredentials

Server

package main

import (
    "context"
    "log"
    "net"

    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/metadata"
    "google.golang.org/grpc/status"

    "github.com/EDDYCJY/go-grpc-example/pkg/gtls"
    pb "github.com/EDDYCJY/go-grpc-example/proto"
)

type SearchService struct {
    auth *Auth
}

func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) {
    if err := s.auth.Check(ctx); err != nil {
        return nil, err
    }
    return &pb.SearchResponse{Response: r.GetRequest() + " Token Server"}, nil
}

const PORT = "9004"

func main() {
    ...
}

type Auth struct {
    appKey    string
    appSecret string
}

func (a *Auth) Check(ctx context.Context) error {
    md, ok := metadata.FromIncomingContext(ctx)
    if !ok {
        return status.Errorf(codes.Unauthenticated, "自定义认证 Token 失败")
    }

    var (
        appKey    string
        appSecret string
    )
    if value, ok := md["app_key"]; ok {
        appKey = value[0]
    }
    if value, ok := md["app_secret"]; ok {
        appSecret = value[0]
    }

    if appKey != a.GetAppKey() || appSecret != a.GetAppSecret() {
        return status.Errorf(codes.Unauthenticated, "自定义认证 Token 无效")
    }

    return nil
}

func (a *Auth) GetAppKey() string {
    return "eddycjy"
}

func (a *Auth) GetAppSecret() string {
    return "20181005"
}

On the Server side, it’s even simpler, actually callingmetadata.FromIncomingContextObtain metadata from the context, and then perform authentication checks in different RPC methods

Verification

Restart server.go and client.go with the following results:

$ go run client.go
2018/10/05 20:59:58 resp: gRPC Token Server

Modify the value of client.go to make the two inconsistent, resulting in invalid results:

$ go run client.go
2018/10/05 21:00:05 client.Search err: rpc error: code = Unauthenticated desc = invalid token
exit status 1

Add too much trouble one by one.

I’m sure you will ask one by one. Is it too much trouble? Have this idea, you should puttype PerRPCCredentials interfaceMake an interceptor

Summary

This chapter is relatively simple. It mainly introduces the custom authentication of RPC method. If you want to do the overall situation, it is recommended to use the interceptor as an example.

References

This series of sample codes

Series catalog