Skip to content

Instantly share code, notes, and snippets.

@thinhdanggroup
Created December 18, 2019 02:44
Show Gist options
  • Save thinhdanggroup/fe8327bbe949d928a1f123994369fdce to your computer and use it in GitHub Desktop.
Save thinhdanggroup/fe8327bbe949d928a1f123994369fdce to your computer and use it in GitHub Desktop.
Exception Translation Interceptor gRPC example
import io.grpc.ForwardingServerCallListener.SimpleForwardingServerCallListener;
import io.grpc.*;
import lombok.extern.slf4j.Slf4j;
import org.lognet.springboot.grpc.GRpcGlobalInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.util.ThrowableAnalyzer;
import java.util.Objects;
@GRpcGlobalInterceptor
@Order(20)
@Slf4j
public class ExceptionTranslationInterceptor implements ServerInterceptor {
@Autowired
private ErrorTranslator errorTranslator;
private ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call,
Metadata headers,
ServerCallHandler<ReqT, RespT> next) {
ServerCall.Listener<ReqT> delegate = next.startCall(call, headers);
return new SimpleForwardingServerCallListener<ReqT>(delegate) {
@Override
public void onHalfClose() {
try {
super.onHalfClose();
log.debug("Chain processed normally");
} catch (Exception e) {
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e);
AuthenticationException authenticationException = (AuthenticationException) throwableAnalyzer
.getFirstThrowableOfType(AuthenticationException.class, causeChain);
if (Objects.nonNull(authenticationException)) {
handleAuthenticationException(authenticationException);
} else {
AccessDeniedException accessDeniedException = (AccessDeniedException) throwableAnalyzer
.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
if (Objects.nonNull(accessDeniedException)) {
handleAccessDeniedException(accessDeniedException);
} else {
handleException(call, e);
}
}
}
}
private void handleAuthenticationException(AuthenticationException exception) {
log.debug("Authentication exception occurred, closing call with UNAUTHENTICATED", exception);
call.close(Status.UNAUTHENTICATED.withDescription(exception.getMessage())
.withCause(exception), new Metadata());
}
private void handleAccessDeniedException(AccessDeniedException exception) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authenticationTrustResolver.isAnonymous(authentication)) {
log.debug("Access is denied (user is anonymous), closing call with UNAUTHENTICATED", exception);
call.close(Status.UNAUTHENTICATED.withDescription("Authentication is required to access this resource")
.withCause(exception), new Metadata());
} else {
log.debug("Access is denied (user is not anonymous), closing call with PERMISSION_DENIED", exception);
call.close(Status.PERMISSION_DENIED.withDescription(exception.getMessage())
.withCause(exception), new Metadata());
}
}
private <ReqT, RespT> void handleException(ServerCall<ReqT, RespT> callArg, Throwable ex) {
ErrorDetail error = errorTranslator.translate(ex);
if (error.getCode() == Code.SERVER_ERROR) {
log.error("Error when handling grpc call.", ex);
} else {
log.warn("Error when handling grpc call.", ex);
}
Status status = errorTranslator
.grpcStatus(ex)
.withDescription(JsonProtoUtils.printGson(error));
callArg.close(status, new Metadata());
}
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment