grpc-spring
grpc-spring copied to clipboard
OAuth requires custom bean configuration to work with grpc
The context
What do you wish to achieve?
Authentication and Authorization not working
What's the problem? What's not working? What's missing and why do you need it?
按照文档配置鉴权和授权都失效了。鉴权只有@Secured 能生效,手动管理 method 不生效。
Do you have any relevant stacktraces or logs of your attempts?
The application's environment
Which versions do you use?
- Spring (boot): 2.2.5.RELEASE
- grpc-java: 5.2.4.RELEASE
- grpc-spring-boot-starter: 2.7.0.RELEASE
- java: version + architecture (64bit?) 11 + 64
- Other relevant libraries...
Additional information
- Did it ever work before? no
- How can we reproduce it? not sure
- Do you have a demo? no
package com.castlery.roma.grpc.config.grpc;
import com.castlery.echo.v1.EchoServiceGrpc;
import java.util.ArrayList;
import java.util.List;
import net.devh.boot.grpc.server.security.authentication.BearerAuthenticationReader;
import net.devh.boot.grpc.server.security.authentication.GrpcAuthenticationReader;
import net.devh.boot.grpc.server.security.check.AccessPredicate;
import net.devh.boot.grpc.server.security.check.AccessPredicateVoter;
import net.devh.boot.grpc.server.security.check.GrpcSecurityMetadataSource;
import net.devh.boot.grpc.server.security.check.ManualGrpcSecurityMetadataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
@Configuration(proxyBeanMethods = false)
@EnableGlobalMethodSecurity(securedEnabled = true, proxyTargetClass = true)
public class GrpcOauthConfiguration {
@Bean
GrpcAuthenticationReader authenticationReader() {
// The actual token class is dependent on your spring-security library (OAuth2/JWT/...)
return new BearerAuthenticationReader(BearerTokenAuthenticationToken::new);
}
@Bean
GrpcSecurityMetadataSource grpcSecurityMetadataSource() {
final ManualGrpcSecurityMetadataSource source = new ManualGrpcSecurityMetadataSource();
source.set(EchoServiceGrpc.getEchoMethod(), AccessPredicate.fullyAuthenticated());
source.setDefault(AccessPredicate.denyAll());
return source;
}
@Bean
AccessDecisionManager accessDecisionManager() {
final List<AccessDecisionVoter<?>> voters = new ArrayList<>();
voters.add(new AccessPredicateVoter());
return new UnanimousBased(voters);
}
}
package com.castlery.roma.grpc.server;
import com.castlery.echo.v1.EchoRequest;
import com.castlery.echo.v1.EchoResponse;
import com.castlery.echo.v1.EchoServiceGrpc;
import com.google.common.base.Strings;
import com.google.protobuf.Any;
import com.google.rpc.Code;
import com.google.rpc.RequestInfo;
import io.grpc.Metadata;
import io.grpc.protobuf.ProtoUtils;
import io.grpc.protobuf.StatusProto;
import io.grpc.stub.StreamObserver;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.UUID;
import javax.persistence.EntityNotFoundException;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.server.interceptor.GlobalServerInterceptorRegistry;
import net.devh.boot.grpc.server.service.GrpcService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
@GrpcService
@Slf4j
public class EchoGrpcService extends EchoServiceGrpc.EchoServiceImplBase {
@Autowired GlobalServerInterceptorRegistry registry;
@Override
public void echo(EchoRequest request, StreamObserver<EchoResponse> responseObserver) {
String hostName = "UNKNOWN";
try {
hostName = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
log.error("Unable to get host name");
}
String message = request.getMessage();
String responseMessage = String.format("Message from [%s]: %s", hostName, message);
if (!Strings.isNullOrEmpty(message)) {
switch (message.length()) {
case 1:
// directly throw runtime exception
throw new EntityNotFoundException("Failed with throwing exception");
case 2:
// directly return with runtime exception, not recommended
responseObserver.onError(new RuntimeException("Failed with direct return"));
break;
case 3:
// use io.grpc.status
RequestInfo requestInfo =
RequestInfo.newBuilder().setRequestId(UUID.randomUUID().toString()).build();
Metadata trailers = new Metadata();
trailers.put(ProtoUtils.keyForProto(requestInfo), requestInfo);
responseObserver.onError(
io.grpc.Status.INTERNAL
.withDescription("Failed with io.grpc.Status")
.asRuntimeException(trailers));
break;
case 4:
// use com.google.rpc.Status
RequestInfo requestInfo2 =
RequestInfo.newBuilder().setRequestId(UUID.randomUUID().toString()).build();
com.google.rpc.Status status =
com.google.rpc.Status.newBuilder()
.setCode(Code.INTERNAL.getNumber())
.setMessage("Failed with com.google.rpc.Status.")
.addDetails(Any.pack(requestInfo2))
.build();
responseObserver.onError(StatusProto.toStatusRuntimeException(status));
break;
default:
EchoResponse response = EchoResponse.newBuilder().setMessage(responseMessage).build();
responseObserver.onNext(response);
responseObserver.onCompleted();
break;
}
} else {
EchoResponse response = EchoResponse.newBuilder().setMessage(responseMessage).build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
}
代码是很简单的 echo service
I try to provide my own authentication provider and this worker out. So Authentication manager is necessary for grpc authentication?
Sorry for the late response.
Did I get your correctly that authentication and authorization is not working? Could you please be a little bit more specific about what exactly is not working? Is there something useful in the logs?
Can you please turn on the debug log for net.devh.boot.grpc.server.security.interceptors
and send it to me?
The logs hopefully show me, whether the authentication is entirely bypassed or whether spring assumes, that it passed.
Manual security bypass
This is surprising, as I have automated tests that verify the correct functionality of that logic.
Once again, I need the interceptor logs and please check whether the AuthorizationCheckingServerInterceptor
bean is created and executed.
I try to provide my own authentication provider and this worker out. So Authentication manager is necessary for grpc authentication?
I assume that you are using spring-security-oauth? Unfortunately, most of the spring-security-oauth implementations bypass the normal spring-security setup and thus don't work out of the box with other libraries. See also https://github.com/yidongnan/grpc-spring-boot-starter/issues/457 I'll add a comment regarding that behavior to the documentation.