gRPC授权访问CA(Certificate Authority

2019-06-20  本文已影响0人  渣渣曦

下载代码:
https://golang.org/src/crypto/tls/generate_cert.go
生成公钥及私钥:

go run generate_cert.go --host server.io

服务端代码server.go:

package main

import (
    "context"
    "crypto/tls"
    "flag"
    "fmt"
    "log"
    "net"
    "strings"

    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/credentials"
    ecpb "google.golang.org/grpc/examples/features/proto/echo"
    "google.golang.org/grpc/metadata"
    "google.golang.org/grpc/status"
)

var (
    errMissingMetadata = status.Errorf(codes.InvalidArgument, "missing metadata")
    errInvalidToken    = status.Errorf(codes.Unauthenticated, "invalid token")
)

var port = flag.Int("port", 50051, "the port to serve on")

func main() {
    flag.Parse()
    fmt.Printf("server starting on port %d...\n", *port)

    cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
    if err != nil {
        log.Fatalf("failed to load key pair: %s", err)
    }
    opts := []grpc.ServerOption{
        // token拦截器
        grpc.UnaryInterceptor(ensureValidToken),
        // Enable TLS for all incoming connections.
        grpc.Creds(credentials.NewServerTLSFromCert(&cert)),
    }
    s := grpc.NewServer(opts...)
    ecpb.RegisterEchoServer(s, &ecServer{})
    lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

type ecServer struct{}

func (s *ecServer) UnaryEcho(ctx context.Context, req *ecpb.EchoRequest) (*ecpb.EchoResponse, error) {
    return &ecpb.EchoResponse{Message: req.Message}, nil
}
func (s *ecServer) ServerStreamingEcho(*ecpb.EchoRequest, ecpb.Echo_ServerStreamingEchoServer) error {
    return status.Errorf(codes.Unimplemented, "not implemented")
}
func (s *ecServer) ClientStreamingEcho(ecpb.Echo_ClientStreamingEchoServer) error {
    return status.Errorf(codes.Unimplemented, "not implemented")
}
func (s *ecServer) BidirectionalStreamingEcho(ecpb.Echo_BidirectionalStreamingEchoServer) error {
    return status.Errorf(codes.Unimplemented, "not implemented")
}

// valid validates the authorization.
func valid(authorization []string) bool {
    if len(authorization) < 1 {
        return false
    }
    token := strings.TrimPrefix(authorization[0], "Bearer ")
    // Token验证代码.
    return token == "some-secret-token"
}

// 验证Token
func ensureValidToken(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    md, ok := metadata.FromIncomingContext(ctx)
    if !ok {
        return nil, errMissingMetadata
    }
    // The keys within metadata.MD are normalized to lowercase.
    // See: https://godoc.org/google.golang.org/grpc/metadata#New
    if !valid(md["authorization"]) {
        return nil, errInvalidToken
    }
    // Continue execution of handler after ensuring a valid token.
    return handler(ctx, req)
}

客户端代码client.go

package main

import (
    "context"
    "flag"
    "fmt"
    "log"
    "time"

    "golang.org/x/oauth2"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    "google.golang.org/grpc/credentials/oauth"
    ecpb "google.golang.org/grpc/examples/features/proto/echo"
)

var addr = flag.String("addr", "localhost:50051", "the address to connect to")

func callUnaryEcho(client ecpb.EchoClient, message string) {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    resp, err := client.UnaryEcho(ctx, &ecpb.EchoRequest{Message: message})
    if err != nil {
        log.Fatalf("client.UnaryEcho(_) = _, %v: ", err)
    }
    fmt.Println("UnaryEcho: ", resp.Message)
}

func main() {
    flag.Parse()

    // Set up the credentials for the connection.
    perRPC := oauth.NewOauthAccess(fetchToken())
    creds, err := credentials.NewClientTLSFromFile("cert.pem", "server.io")
    if err != nil {
        log.Fatalf("failed to load credentials: %v", err)
    }
    opts := []grpc.DialOption{
        // In addition to the following grpc.DialOption, callers may also use
        // the grpc.CallOption grpc.PerRPCCredentials with the RPC invocation
        // itself.
        // See: https://godoc.org/google.golang.org/grpc#PerRPCCredentials
        grpc.WithPerRPCCredentials(perRPC),
        // oauth.NewOauthAccess requires the configuration of transport
        // credentials.
        grpc.WithTransportCredentials(creds),
    }

    conn, err := grpc.Dial(*addr, opts...)
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    rgc := ecpb.NewEchoClient(conn)

    callUnaryEcho(rgc, "hello world")
}

// fetchToken simulates a token lookup and omits the details of proper token
// acquisition. For examples of how to acquire an OAuth2 token, see:
// https://godoc.org/golang.org/x/oauth2
func fetchToken() *oauth2.Token {
    return &oauth2.Token{
        AccessToken: "some-secret-token",
    }
}
上一篇 下一篇

猜你喜欢

热点阅读