Go language microservice framework practice: 7.TLS authentication and Token authentication

Posted by t0ta11 on Fri, 19 Jun 2020 10:31:10 +0200

TLS authentication and Token authentication

@author: Davie
Copyright: Beijing Qianfeng Internet Technology Co., Ltd

In the last lesson, we learned four flow patterns of gRPC go framework. In the actual production environment, a full-featured service includes not only the basic function of method call and data interaction, but also authorization authentication, data tracking, load balancing and other aspects. In this lesson, we will take a look at how to implement authorization authentication and how to intercept in addition to the gRPC call process.

I. authorized certification

By default, gRPC supports two authorization methods: SSL/TLS authentication and Token based authentication.

1.1 SSL/TLS authentication mode

SSL full name is Secure Sockets Layer, also known as Secure Sockets Layer. It is a standard security protocol used to establish the encrypted link between the client and the server in the communication process. The full name of TLS is Transport Layer Security, and TLS is an upgraded version of SSL. In the process of using, it is often used to combine SSL and TLS to write SSL/TLS. In short, SSL/TLS is a secure protocol for encryption in network communication.

1.1.1 SSL/TLS working principle

Using SSL/TLS protocol to encrypt the communication connection is realized by asymmetric encryption. The so-called asymmetric encryption is also known as public key encryption. The key pair consists of public key and private key. The private key and the public key exist in pairs. The private key is generated by the private key. The public key can be made public and the private key can be kept properly.

In the encryption process: if the client wants to initiate a link to the server, it will first request the public key to be encrypted from the server. After obtaining the public key, the client uses the public key to encrypt the information, the server receives the encrypted information, uses the private key to decrypt the information and carry out other subsequent processing, completing the whole channel encryption and data transmission process.

1.1.2 production certificate

You can install openssl on your own computer and generate the corresponding certificate.

openssl ecparam -genkey -name secp384r1 -out server.key
openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650

1.1.3 programming the server

type MathManager struct {
}

func (mm *MathManager) AddMethod(ctx context.Context, request *message.RequestArgs) (response *message.Response, err error) {
    fmt.Println(" Server Add method ")
    result := request.Args1 + request.Args2
    fmt.Println(" The calculation results are as follows:", result)
    response = new(message.Response)
    response.Code = 1;
    response.Message = "Execution successful"
    return response, nil
}

func main() {

    //TLS certification
    creds, err := credentials.NewServerTLSFromFile("./keys/server.pem", "./keys/server.key")
    if err != nil {
        grpclog.Fatal("Failed to load in certificate file", err)
    }

    //Instantiate grpc server and enable TLS authentication
    server := grpc.NewServer(grpc.Creds(creds))

    message.RegisterMathServiceServer(server, new(MathManager))

    lis, err := net.Listen("tcp", ":8092")
    if err != nil {
        panic(err.Error())
    }
    server.Serve(lis)
}

1.1.3 programming client

func main() {

    //TLS connection
    creds, err := credentials.NewClientTLSFromFile("./keys/server.pem", "go-grpc-example")
    if err != nil {
        panic(err.Error())
    }
    //1. Dail connection
    conn, err := grpc.Dial("localhost:8092", grpc.WithTransportCredentials(creds))
    if err != nil {
        panic(err.Error())
    }
    defer conn.Close()

    serviceClient := message.NewMathServiceClient(conn)

    addArgs := message.RequestArgs{Args1: 3, Args2: 5}

    response, err := serviceClient.AddMethod(context.Background(), &addArgs)
    if err != nil {
        grpclog.Fatal(err.Error())
    }

    fmt.Println(response.GetCode(), response.GetMessage())
}

1.2 Token based authentication

1.2.1 introduction to token authentication

In the process of web application development, we often use another authentication method for authentication, which is token authentication. Token based authentication is stateless and does not need to store the user information service in the server or session.

1.2.2 Token authentication process

The main process of authentication based on token authentication is: before sending a request, the client first sends a request to the server, and the server returns a generated token to the client. The client saves the token for each subsequent request with the token parameter. Before the server processes the request, it will first verify the token. Only when the token verification is successful, it will process and return the relevant data.

1.2.3 custom Token authentication of grpc

In gRPC, developers are allowed to customize their own authentication rules and pass the

grpc.WithPerRPCCredentials()

Set custom authentication rules. The WithPerRPCCredentials method receives a parameter of type PerRPCCredentials. Looking further, we can see that PerRPCCredentials is an interface, which is defined as follows:

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

Therefore, developers can implement the above interface to define their own token information.

In this case, our custom token authentication structure is as follows:

//token authentication 
type TokenAuthentication struct {
    AppKey    string
    AppSecret string
}

//Organization token information
func (ta *TokenAuthentication) RequestMetaData(ctx context.Context, uri ...string) (map[string]string, error) {
    return map[string]string{
        "appid":    ta.AppKey,
        "appkey": ta.AppSecret,
    }, nil
}

//Security transmission based on TLS authentication
func (a *TokenAuthentication) RequireTransportSecurity() bool {
    return true
}

Note that the appid and appkey fields in the RequestMetaData method need to be kept in lowercase, not uppercase. The RequireTransportSecurity method is used to set whether or not a secure transfer is based on tls authentication.

When the client connects, we pass in the custom token authentication information as a parameter.

auth := TokenAuthentication{
        AppKey:    "hello",
        AppSecret: "20190812",
}
conn, err := grpc.Dial("localhost:8093", grpc.WithTransportCredentials(creds), grpc.WithPerRPCCredentials(&auth))
if err != nil {
    panic(err.Error())
}

1.2.4 server

In the calling method of the server, we can judge the token request parameters and obtain the token authentication information through metadata. The specific implementation is as follows:

func (mm *MathManager) AddMethod(ctx context.Context, request *message.RequestArgs) (response *message.Response, err error) {

    //Through metadata
    md, exist := metadata.FromIncomingContext(ctx)
    if !exist {
        return nil, status.Errorf(codes.Unauthenticated, "nothing Token Certification information")
    }

    var appKey string
    var appSecret string

    if key, ok := md["appid"]; ok {
        appKey = key[0]
    }

    if secret, ok := md["appkey"]; ok {
        appSecret = secret[0]
    }

    if appKey != "hello" || appSecret != "20190812" {
        return nil, status.Errorf(codes.Unauthenticated, "Token wrongful")
    }
    fmt.Println(" Server Add method ")
    result := request.Args1 + request.Args2
    fmt.Println(" The calculation results are as follows:", result)
    response = new(message.Response)
    response.Code = 1;
    response.Message = "Execution successful"
    return response, nil
}

Finally, running the project, token authentication succeeded. When the client modifies the token information and runs it again, it will find that the prompt token is illegal.

Topics: SSL OpenSSL Programming network