​​Go语言:拦截器的使用

需求

在上节课程中,我们学习使用了gRPC框架中的两种认证方式:TLS验证和Token验证。

但是,在服务端的方法中,每个方法都要进行token的判断。程序效率太低,可以优化一下处理逻辑,在调用服务端的具体方法之前,先进行拦截,并进行token验证判断,这种方式称之为拦截器处理。

除了此处的token验证判断处理以外,还可以进行日志处理等。

Interceptor

使用拦截器,首先需要注册。 在grpc中编程实现中,可以在NewSever时添加拦截器设置,grpc框架中可以通过UnaryInterceptor方法设置自定义的拦截器,并返回ServerOption。具体代码如下:

grpc.UnaryInterceptor()

UnaryInterceptor()接收一个UnaryServerInterceptor类型,继续查看源码定义,可以发现UnaryServerInterceptor是一个func,定义如下:

type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)

通过以上代码,如果开发者需要注册自定义拦截器,需要自定义实现UnaryServerInterceptor的定义。

自定义UnaryServerInterceptor

接下来就自定义实现func,符合UnaryServerInterceptor的标准,在该func的定义中实现对token的验证逻辑。自定义实现func如下:

func TokenInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {

    //通过metadata
    md, exist := metadata.FromIncomingContext(ctx)
    if !exist {
        return nil, status.Errorf(codes.Unauthenticated, "无Token认证信息")
    }

    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 不合法")
    }
    //通过token验证,继续处理请求
    return handler(ctx, req)
}

在自定义的TokenInterceptor方法定义中,和之前在服务的方法调用的验证逻辑一致,从metadata中取出请求头中携带的token认证信息,并进行验证是否正确。如果token验证通过,则继续处理请求后续逻辑,后续继续处理可以由grpc.UnaryHandler进行处理。grpc.UnaryHandler同样是一个方法,其具体的实现就是开发者自定义实现的服务方法。grpc.UnaryHandler接口定义源码定义如下:

type UnaryHandler func(ctx context.Context, req interface{}) (interface{}, error)

拦截器注册

在服务端调用grpc.NewServer时进行拦截器的注册。详细如下:

server := grpc.NewServer(grpc.Creds(creds), grpc.UnaryInterceptor(TokenInterceptor))

项目运行

依次运行server.go程序和client.go程序,可以得到程序运行的正确结果。修改token携带值,可以验证token非法情况的拦截器效果。