-
Grpc Spring Security - 3) Grpc Client에서 header를 포함한 grpc 호출하기Spring 2024. 9. 21. 14:45
이전 글에서 grpcService에서는 인증, 인가 처리를 구현했고, 이번에는 grpcClient에서 토큰을 포함해서 grpc를 호출할 수 있도록 구현해보자.
grpcService의 인증 인가 구현 지난 글
↓
https://dodop-blog.tistory.com/472
GrpcClient에 메타데이터 넣어서 보내기 위해서는 Interceptor를 별도로 구현하고 metadata를 넣어서 보내도록 설정하거나, Service를 정의하는 시점에 Metadata를 넣어서 빈으로 등록해서 활용하는 방안 2가지가 있다.
Interceptor를 활용한 방안
단순 토큰을 넣는 작업이 아니라면 interceptor를 구현해서 활용할 수 있다.
먼저 메타데이터를 넣는 인터셉터를 구현한다.
open class GrpcClientHeaderInterceptor(private val requestHeaders: Map<String, String>) : ClientInterceptor { override fun <ReqT : Any, RespT : Any> interceptCall( method: MethodDescriptor<ReqT, RespT>, callOptions: CallOptions, next: Channel, ): ClientCall<ReqT, RespT> { val clientCall = next.newCall(method, callOptions) return object : ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(clientCall) { override fun start( responseListener: Listener<RespT>, headers: Metadata, ) { requestHeaders.forEach { if (it.key == "Authorization") { headers.put(Metadata.Key.of(it.key, Metadata.ASCII_STRING_MARSHALLER), "Bearer ${it.value}") } else { headers.put(Metadata.Key.of(it.key, Metadata.ASCII_STRING_MARSHALLER), it.value) } } super.start(responseListener, headers) } } } }
이를 활용하는 channel을 만든다.
yaml에 host와 port를 지정해준다.
grpc: client: grpc-service: address: static://localhost:9090 negotiationType: plaintext
channel을 빈으로 등록한다.
@Bean fun channel( @Value("\${grpc.client.grpc-service.address}") address: String, ): Channel { val uri = URI.create(address) val channelBuilder = NettyChannelBuilder.forAddress(uri.host, uri.port) .defaultLoadBalancingPolicy("round_robin") .usePlaintext() .build() return ClientInterceptors.intercept( channel, mutableListOf( GrpcClientHeaderInterceptor(mapOf("Authorization" to "test")), ), ) }
다음과 같이 사용할 수 있다.
@Service @GrpcComponent class GrpcCustomService( val channel: Channel ) { private val bookAuthorService: BookAuthorServiceGrpc.BookAuthorServiceBlockingStub by lazy { BookAuthorServiceGrpc.newBlockingStub(channel) } }
빈으로 등록하기
클라이언트를 고정된 빈으로 등록할 수도 있다.
먼저 yaml에 주소를 등록한다.
grpc: client: grpc-service: address: static://localhost:9090 negotiationType: plaintext
빈으로 channel을 등록해준다.
@Bean fun managedChannel( @Value("\${grpc.client.grpc-service.address}") address: String, ): ManagedChannel { val uri = URI.create(address) return NettyChannelBuilder.forAddress(uri.host, uri.port) .defaultLoadBalancingPolicy("round_robin") .usePlaintext() .build() }
이제 bookService를 메타데이터를 이용해서 빈으로 등록한다.
@Bean fun bookService(managedChannel: ManagedChannel, metadata: Metadata): BookAuthorServiceGrpc.BookAuthorServiceBlockingStub { val metadata = Metadata() val key = io.grpc.Metadata.Key.of("Authorization", io.grpc.Metadata.ASCII_STRING_MARSHALLER) metadata.put(key, "Bearer token") val stub = BookAuthorServiceGrpc.newBlockingStub(managedChannel) stub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)) return stub }
등록된 빈을 클라이언트가 활용하면 된다!
@Service @GrpcComponent class GrpcCustomService( val bookService: BookAuthorServiceGrpc.BookAuthorServiceBlockingStub ) { }
grpc 통신 메타데이터 확인해보기
grpc 통신에서 주고받는 데이터를 확인하려면 다음과 같이 yaml에 로깅설정을 넣어주면 된다.
logging: level: root: info io.grpc: DEBUG # gRPC 라이브러리 관련 로그 io.grpc.netty.shaded: DEBUG # Netty 기반 gRPC 로그 org.springframework.grpc: DEBUG # Spring Boot gRPC 로그 (선택 사항)
+ 참고 ✨)
- https://no-delay-code.tistory.com/211
- https://github.com/grpc/grpc-java/blob/master/auth/src/main/java/io/grpc/auth/ClientAuthInterceptor.java
- https://grpc.io/docs/guides/auth/
- https://brunch.co.kr/@mobiinside/1817
- https://github.dev/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/healthservice/HealthServiceServer.java
'Spring' 카테고리의 다른 글
동시 삭제 요청으로 인한 StaleObjectStateException 해결 - redisson lock 적용기 (feat. Spring AOP, applicationEventListener) (2) 2024.10.01 Grpc + Spring : 예외 처리 구현 (0) 2024.09.22 Grpc Spring Security - 2) Grpc Service에 인증, 인가 구현하기 (0) 2024.09.21 Grpc Spring Security - 1) GrpcSpringSecurity의 인증, 인가 (0) 2024.09.21 SpringBatch) 스프링 배치 5의 변경점 (1) 2024.03.31