간단한 gRPC 서비스가 있습니다:
message GetUserRequest { string id = 1; } message GetUserResponse { string id = 1; string name = 2; string email = 3; } service UsersService { rpc GetUser(GetUserRequest) returns (GetUserResponse); }
gRPC는 여러 면에서 훌륭하고, 성능도 훌륭하고, 생태계도 비교할 수 없습니다.
하지만 무엇보다 중요한 것은 입력된 계약서가 제공된다는 것입니다.
저는 백엔드 엔지니어입니다. 모바일 및 웹 친구들과 함께 앉아서 논의하고 합의를 도출한 다음 모의 구현을 위해 Flutter 및 es로 스텁 클라이언트 코드를 생성하고 3일 후에 다시 그룹화할 수 있습니다.
좋은 공연의 날!
하지만 잠깐만요. 뭔가 빠졌네요!
service UsersService { rpc GetUser(GetUserRequest) returns (GetUserResponse); }
GetUser 메소드에는 거의 모든 것이 있지만 인증 요구 사항을 설명하기에는 아직 충분하지 않습니다.
그리고 RBAC 규칙을 설명하는 역할 부분만 빠졌습니다.
안타깝네요. 우리가 뭔가를 할 수 있다면, 형식이 안전하고 일반적인 일을 할 수만 있다면... ?
프로토 디스크립터의 도움으로 당신의 소원이 이루어집니다
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 }; } }
농담입니다. 밤이 늦어서 오전 8시 이전에 사무실에 도착해야 하나요?
메소드 설명자 로드:
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") }
access_control 옵션을 찾으세요:
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") }
새로 생성된 인터셉터를 백엔드에 추가하거나 추가하세요
func newUsersServer() *grpc.Server { svc := grpc.NewServer(grpc.UnaryInterceptor(interceptors.RBACUnaryInterceptor)) usersv1.RegisterUsersServiceServer(svc, &usersServer{}) return svc }
그런 다음 테스트해 보세요.
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
마지막으로 테스트 가능한 예제가 포함된 예제 코드가 https://github.com/nvcnvn/grpc-methods-descriptor-example에 게시되었습니다.
위 내용은 RBAC를 사용하여 proto 정의에서 바로 gRPC 서비스를 보호하세요.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!