验证请求
在本节以及接下来的部分,我们将简化当前的中间件。首先,我们将从简化身份验证过程开始。我们在上一章中看到,我们可以轻松地为检查请求头中的身份验证令牌创建拦截器。在本节中,我们将更进一步,使其变得更简单。
gRPC 支持通过 RBAC 策略重试请求的身份验证,而无需第三方库。然而,配置相当冗长且文档不太完善。如果你有兴趣尝试,可以查看以下示例: gRPC RBAC 示例。 |
之前,当我们编写拦截器时,我们需要为单次拦截器创建如下的函数:
func unaryAuthInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error)
以及为流式拦截器创建类似如下的函数:
func streamAuthInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error
虽然这给我们提供了很多关于调用、上下文等信息,但这也使得代码变得简洁,并且我们需要考虑如何在流式拦截器和单次拦截器之间共享公共业务逻辑。
通过我们将要添加的中间件,我们将能够专注于我们的逻辑,并像以前一样轻松地注册拦截器。这个中间件是来自 GitHub 仓库 go-grpc-middleware 的 auth 中间件( go-grpc-middleware )。它将让我们摆脱为拦截器编写复杂身份验证函数定义的烦恼,并允许我们通过预定义的拦截器直接注册我们的 validateAuthToken
函数。
为了开始,我们将在 server
文件夹中获取依赖项:
$ go get github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth
然后,我们将在 server/interceptors.go
文件中移除 unaryAuthInterceptor
和 streamAuthInterceptor
,因为新的身份验证中间件将为我们处理所有内容。
最后,我们将前往 server/main.go
,在这里我们将用 auth.UnaryServerInterceptor
和 auth.StreamServerInterceptor
替换旧的拦截器。这两个拦截器需要一个 AuthFunc
,它基本上代表了身份验证逻辑。在我们的例子中,我们将传递给它们我们的 validateAuthToken
。
AuthFunc
类型如下所示:
type AuthFunc func(ctx context.Context) (context.Context, error)
因此,我们需要稍微修改 validateAuthToken
函数,以便返回一个上下文和/或错误。我们的新函数将如下所示:
func validateAuthToken(ctx context.Context) (context.Context, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Errorf(/*...*/)
}
if t, ok := md["auth_token"]; ok {
switch {
case len(t) != 1:
return nil, status.Errorf(/*...*/)
case t[0] != "authd":
return nil, status.Errorf(/*...*/)
}
} else {
return nil, status.Errorf(/*...*/)
}
return ctx, nil
}
这让我们能够在 gRPC 服务器中注册 validateAuthToken
。我们的新 main
函数将如下所示:
import (
//...
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
)
func main() {
//...
opts := []grpc.ServerOption{
//...
grpc.ChainUnaryInterceptor(
auth.UnaryServerInterceptor(validateAuthToken),
unaryLogInterceptor),
grpc.ChainStreamInterceptor(
auth.StreamServerInterceptor(validateAuthToken),
streamLogInterceptor),
}
//...
}
现在,我们应该能够运行服务器:
$ go run ./server 0.0.0.0:50051
listening at 0.0.0.0:50051
然后运行客户端:
$ go run ./client 0.0.0.0:50051
我们没有遇到任何错误,程序也能像以前一样工作。然而,为了测试中间件是否正常工作,我们可以暂时修改客户端拦截器中的身份验证头部(client/interceptors.go
):
const authTokenValue string = "notauthd"
如果我们重新运行客户端,应该会得到以下错误:
$ go run ./client 0.0.0.0:50051
--------ADD--------
rpc error: code = Unauthenticated desc = incorrect auth_token
这证明了我们的中间件按预期工作,并且我们可以依赖 validateAuthToken
来进行身份验证检查。
Bazel
为了使用 Bazel 运行该程序,我们需要更新我们的依赖并将新的依赖链接到 server/BUILD.bazel
中的 server_lib
目标。因此,我们首先运行 gazelle-updaterepos
命令,这将获取 go-grpc-middleware
依赖:
$ bazel run //:gazelle-update-repos
完成后,我们现在可以让 gazelle
命令将 go-grpc-middleware
依赖包括到目标中:
$ bazel run //:gazelle
最后,我们将能够运行我们的服务器:
$ bazel run //server:server 0.0.0.0:50051
listening at 0.0.0.0:50051
如果客户端使用错误的身份验证令牌,应该会显示以下消息:
$ bazel run //client:client 0.0.0.0:50051
--------ADD--------
rpc error: code = Unauthenticated desc = incorrect auth_token
总结来说,在本节中,我们看到通过使用 go-grpc-middleware
包,我们可以简化身份验证拦截器。它让我们专注于实际的业务逻辑,而不必担心如何编写可以与 gRPC 注册的拦截器。