我使用 gRPC-Gateway 作为 RESTful API 和 gRPC 服务器。这是我的代码:
func main() {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// rest server
go func() {
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
err := pb.RegisterXYZHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
if err != nil {
// Err
}
if err := http.ListenAndServe(":8080", mux); err != nil {
// Err
}
}()
// grpc server
l, err := net.Listen("tcp", ":9090")
if err != nil {
// Err
}
xyzSvc := server.Init()
s := grpc.NewServer(
grpc.UnaryInterceptor(
server.AuthenticateInterceptor(xyzSvc),
),
)
pb.RegisterXYZServiceServer(s, xyzSvc)
fmt.Println("GRPC on port 9090, REST Server on port 8080")
if err := s.Serve(l); err != nil {
// Err
}
}
这里第
server.AuthenticateInterceptor(xyzSvc)
行添加了身份验证,效果很好。
但问题是我想添加具有不同端点的 RBAC(角色基础访问控制)。比如:
mux.HandlePath("GET", "/v1/users", AuthMiddleware(RBACMiddleware("user")))
mux.HandlePath("POST", "/v1/users", AuthMiddleware(RBACMiddleware("admin")))
我知道我不能这样做,因为我的端点已经由 gRPC-Gateway 根据
*.proto
文件生成了。
有什么想法可以通过 gRPC-Gateway 添加 RBAC 吗?
要使用 gRPC-Gateway 生成的 RESTful API 实现 RBAC(基于角色的访问控制),您需要将 RBAC 逻辑集成到 gRPC 服务器的拦截器中,因为 gRPC-Gateway 充当转换 RESTful 的反向代理HTTP API 转入 gRPC 调用。
通过在 gRPC 级别处理 RBAC,您可以确保无论客户端是直接使用 REST 还是 gRPC,都强制实施访问控制。
这将涉及:
NguyenTrungTin/go-grpc-boilerplate
pkg/interceptor/auth.go
):
假设 Go 1.20+ 和 generic slices.Contains() 可用性:
package server
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"slices"
)
// AuthenticateInterceptor authenticates requests
func AuthenticateInterceptor(svc *XYZService) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
// Authentication logic here
// Assume we extract user info and roles from the request and add it to the context
ctx = context.WithValue(ctx, "roles", []string{"user", "admin"}) // Example: Add roles to context
return handler(ctx, req)
}
}
// RBACInterceptor checks if the user has the required role to access the endpoint
func RBACInterceptor(requiredRole string) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
roles, ok := ctx.Value("roles").([]string)
if !ok || !slices.Contains(roles, requiredRole) {
return nil, status.Errorf(codes.PermissionDenied, "access denied")
}
return handler(ctx, req)
}
}