Kami mempunyai perkhidmatan gRPC yang mudah:
message GetUserRequest { string id = 1; } message GetUserResponse { string id = 1; string name = 2; string email = 3; } service UsersService { rpc GetUser(GetUserRequest) returns (GetUserResponse); }
gRPC hebat dalam banyak cara, prestasinya hebat, ekosistemnya tidak boleh dibandingkan.
Tetapi imho, atas semua itu adalah kontrak bertaip yang disediakannya.
Saya seorang jurutera bahagian belakang, saya dan rakan mudah alih dan web saya boleh duduk, berbincang, membuat perjanjian, kemudian kami menjana kod klien stub dalam flutter dan es untuk pelaksanaan olok-olok, kumpul semula selepas 3 hari.
Hari yang baik dan berprestasi!
Tetapi tunggu, kami terlepas sesuatu!
service UsersService { rpc GetUser(GetUserRequest) returns (GetUserResponse); }
Kaedah GetUser hampir mempunyai segala-galanya, tetapi masih tidak mencukupi untuk menerangkan keperluan kebenaran.
Dan kami hanya kehilangan bahagian Peranan untuk menerangkan peraturan RBAC.
Terlalu buruk, kita semakin hampir, hanya jika kita boleh melakukan sesuatu, sesuatu jenis selamat, sesuatu yang generik... ?
Hasrat anda akan menjadi kenyataan, dengan bantuan deskriptor proto
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 }; } }
Gurau saja, malam sudah larut dan saya perlu berada di pejabat sebelum pukul 08:00 ?
Muatkan deskriptor kaedah:
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") }
Cari pilihan kawalan_akses kami:
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") }
Tambah atau tambahkan pemintas yang baru dibuat pada hujung belakang anda
func newUsersServer() *grpc.Server { svc := grpc.NewServer(grpc.UnaryInterceptor(interceptors.RBACUnaryInterceptor)) usersv1.RegisterUsersServiceServer(svc, &usersServer{}) return svc }
Kemudian ujinya:
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
Akhir sekali, contoh kod dengan Contoh yang boleh diuji diterbitkan di sini https://github.com/nvcnvn/grpc-methods-descriptor-example
Atas ialah kandungan terperinci Gunakan RBAC untuk melindungi perkhidmatan gRPC anda tepat pada definisi proto. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!