diff --git a/samples/grpc-tomcat-secure/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java b/samples/grpc-tomcat-secure/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java index f08b750..d2af82c 100644 --- a/samples/grpc-tomcat-secure/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java +++ b/samples/grpc-tomcat-secure/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java @@ -3,8 +3,6 @@ package org.springframework.grpc.sample; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import java.util.Base64; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.jupiter.api.Test; @@ -18,20 +16,13 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Lazy; import org.springframework.grpc.client.ChannelBuilderOptions; import org.springframework.grpc.client.GrpcChannelFactory; +import org.springframework.grpc.client.security.BasicAuthenticationInterceptor; import org.springframework.grpc.sample.proto.HelloReply; import org.springframework.grpc.sample.proto.HelloRequest; import org.springframework.grpc.sample.proto.SimpleGrpc; -import org.springframework.grpc.server.security.GrpcSecurity; -import org.springframework.grpc.test.LocalGrpcPort; import org.springframework.test.annotation.DirtiesContext; -import io.grpc.CallOptions; -import io.grpc.Channel; -import io.grpc.ClientCall; -import io.grpc.ClientInterceptor; -import io.grpc.ForwardingClientCall.SimpleForwardingClientCall; import io.grpc.Status.Code; -import io.grpc.MethodDescriptor; import io.grpc.StatusRuntimeException; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -79,22 +70,9 @@ public class GrpcServerApplicationTests { @Lazy SimpleGrpc.SimpleBlockingStub basic(GrpcChannelFactory channels, @LocalServerPort int port) { return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:" + port, - ChannelBuilderOptions.defaults().withCustomizer((__, channel) -> { - channel.intercept(new ClientInterceptor() { - @Override - public ClientCall interceptCall( - MethodDescriptor method, CallOptions callOptions, Channel next) { - return new SimpleForwardingClientCall(next.newCall(method, callOptions)) { - public void start(ClientCall.Listener responseListener, - io.grpc.Metadata headers) { - headers.put(GrpcSecurity.AUTHORIZATION_KEY, - "Basic " + Base64.getEncoder().encodeToString("user:user".getBytes())); - super.start(responseListener, headers); - }; - }; - } - }); - }))); + ChannelBuilderOptions.defaults() + .withCustomizer((authority, channel) -> channel + .intercept(new BasicAuthenticationInterceptor("user", "user"))))); } @Bean diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/client/security/BasicAuthenticationInterceptor.java b/spring-grpc-core/src/main/java/org/springframework/grpc/client/security/BasicAuthenticationInterceptor.java new file mode 100644 index 0000000..cc38486 --- /dev/null +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/client/security/BasicAuthenticationInterceptor.java @@ -0,0 +1,62 @@ +/* + * Copyright 2024-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.grpc.client.security; + +import java.util.Base64; + +import org.springframework.grpc.server.security.GrpcSecurity; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall.SimpleForwardingClientCall; +import io.grpc.MethodDescriptor; + +/** + * An interceptor that adds basic authentication headers to gRPC client calls. This + * interceptor is used to authenticate gRPC client calls with a username and password. The + * username and password are encoded using Base64 and added to the Authorization header. + * + * @author Dave Syer + */ +public class BasicAuthenticationInterceptor implements ClientInterceptor { + + private final String username; + + private final String password; + + public BasicAuthenticationInterceptor(String username, String password) { + this.username = username; + this.password = password; + } + + @Override + public ClientCall interceptCall(MethodDescriptor method, + CallOptions callOptions, Channel next) { + return new SimpleForwardingClientCall(next.newCall(method, callOptions)) { + public void start(ClientCall.Listener responseListener, io.grpc.Metadata headers) { + headers.put(GrpcSecurity.AUTHORIZATION_KEY, + "Basic " + Base64.getEncoder() + .encodeToString((BasicAuthenticationInterceptor.this.username + ":" + + BasicAuthenticationInterceptor.this.password) + .getBytes())); + super.start(responseListener, headers); + }; + }; + } + +}