Wir haben einen einfachen gRPC-Dienst:
message GetUserRequest { string id = 1; } message GetUserResponse { string id = 1; string name = 2; string email = 3; } service UsersService { rpc GetUser(GetUserRequest) returns (GetUserResponse); }
gRPC ist in vielerlei Hinsicht großartig, seine Leistung ist großartig, sein Ökosystem ist unvergleichlich.
Aber meiner Meinung nach gibt es darüber hinaus noch einen schriftlichen Vertrag.
Ich bin ein Backend-Ingenieur, ich und meine mobilen und Web-Freunde können uns zusammensetzen, diskutieren, eine Vereinbarung treffen, dann generieren wir den Stub-Client-Code in Flutter und Es für die Scheinimplementierung und gruppieren uns nach 3 Tagen neu.
Ein guter und leistungsorientierter Tag!
Aber Moment, uns fehlt etwas!
service UsersService { rpc GetUser(GetUserRequest) returns (GetUserResponse); }
Die GetUser-Methode hat fast alles, aber immer noch nicht genug, um die Autorisierungsanforderungen zu beschreiben.
Und uns fehlt nur der Rollenteil, um eine RBAC-Regel zu beschreiben.
Schade, wir kommen so nah dran, nur wenn wir etwas tun können, etwas Typsicheres, etwas Generisches ... ?
Ihr Wunsch wird mit Hilfe des Proto-Deskriptors wahr
enum Role { ROLE_UNSPECIFIED = 0; ROLE_CUSTOMER = 1; ROLE_ADMIN = 2; } message RoleBasedAccessControl { repeated Role allowed_roles = 1; bool allow_unauthenticated = 2; } extend google.protobuf.MethodOptions { optional RoleBasedAccessControl access_control = 90000; // I don't know about this 90000, seem as long as it's unique }
service UsersService { rpc GetUser(GetUserRequest) returns (GetUserResponse) { option (components.rbac.v1.access_control) = { allowed_roles: [ ROLE_CUSTOMER, ROLE_ADMIN ] allow_unauthenticated: false }; } rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse) { option (components.rbac.v1.access_control) = { allowed_roles: [ROLE_ADMIN] allow_unauthenticated: false }; } }
Nur ein Scherz, es ist spät in der Nacht und ich muss vor 08:00 Uhr im Büro sein?
Laden Sie den Methodendeskriptor:
func RBACUnaryInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { // the info give us: /components.users.v1.UsersService/GetUser // we need to convert it to components.users.v1.UsersService.GetUser methodName := strings.Replace(strings.TrimPrefix(info.FullMethod, "/"), "/", ".", -1) desc, err := protoregistry.GlobalFiles.FindDescriptorByName(protoreflect.FullName(methodName)) if err != nil { return nil, status.Errorf(codes.Internal, "method not found descriptor") } method, ok := desc.(protoreflect.MethodDescriptor) if !ok { return nil, status.Errorf(codes.Internal, "some hoe this is not a method") }
Finden Sie unsere Option zur Zugriffskontrolle:
var policy *rbacv1.RoleBasedAccessControl method.Options().ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { if fd.FullName() != rbacv1.E_AccessControl.TypeDescriptor().FullName() { // continue finding the AccessControl field return true } b, err := proto.Marshal(v.Message().Interface()) if err != nil { // TODO: better handle this as an Internal error // but for now, just return PermissionDenied return false } policy = &rbacv1.RoleBasedAccessControl{} if err := proto.Unmarshal(b, policy); err != nil { // same as above, better handle this as an Internal error return false } // btw I think this policy can be cached return false }) if policy == nil { // secure by default, DENY_ALL if no control policy is found return nil, status.Errorf(codes.PermissionDenied, "permission denied") }
Fügen Sie den neu erstellten Interceptor zu Ihrem Backend hinzu oder hängen Sie ihn an
func newUsersServer() *grpc.Server { svc := grpc.NewServer(grpc.UnaryInterceptor(interceptors.RBACUnaryInterceptor)) usersv1.RegisterUsersServiceServer(svc, &usersServer{}) return svc }
Dann testen Sie es:
peasantCtx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("role", "ROLE_CUSTOMER")) _, err = client.GetUser(peasantCtx, &usersv1.GetUserRequest{}) fmt.Println(status.Code(err)) _, err = client.DeleteUser(peasantCtx, &usersv1.DeleteUserRequest{}) fmt.Println(status.Code(err)) knightlyAdminCtx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("role", "ROLE_ADMIN")) _, err = client.GetUser(knightlyAdminCtx, &usersv1.GetUserRequest{}) fmt.Println(status.Code(err)) _, err = client.DeleteUser(knightlyAdminCtx, &usersv1.DeleteUserRequest{}) fmt.Println(status.Code(err)) // Output: // OK // PermissionDenied // OK // OK
Schließlich wird hier ein Beispielcode mit testbarem Beispiel veröffentlicht: https://github.com/nvcnvn/grpc-methods-descriptor-example
Das obige ist der detaillierte Inhalt vonVerwenden Sie RBAC, um Ihren gRPC-Dienst direkt bei der Prototypdefinition zu schützen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!