Reorganize interceptor

This commit is contained in:
Dave Syer
2025-01-23 13:39:38 +00:00
parent 5433d422c0
commit 6cf4a74384
6 changed files with 29 additions and 59 deletions

View File

@@ -151,7 +151,7 @@ public class GrpcServerApplicationTests {
@Lazy
SimpleGrpc.SimpleBlockingStub basic(GrpcChannelFactory channels) {
return SimpleGrpc.newBlockingStub(channels.createChannel("basic", ChannelBuilderOptions.defaults()
.withInterceptors(List.of(new BasicAuthenticationInterceptor("user", "user")))));
.withInterceptors(List.of(new BasicAuthenticationInterceptor("user", "user")))));
}
@Bean

View File

@@ -26,7 +26,6 @@ import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import io.grpc.ForwardingServerCallListener;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCall.Listener;
@@ -37,11 +36,11 @@ import io.grpc.ServerInterceptor;
* An interceptor that extracts the authentication credentials from the gRPC request
* headers and metadata, authenticates the user, and sets the authentication in the
* SecurityContext. This interceptor should be registered with the gRPC server to handle
* authentication.
* authentication and authorization for gRPC requests.
*
* @author Dave Syer
*/
public class AuthenticationServerInterceptor implements ServerInterceptor, Ordered {
public class AuthenticationProcessInterceptor implements ServerInterceptor, Ordered {
private final AuthenticationManager authenticationManager;
@@ -54,7 +53,7 @@ public class AuthenticationServerInterceptor implements ServerInterceptor, Order
return GrpcSecurity.CONTEXT_FILTER_ORDER - 10;
}
public AuthenticationServerInterceptor(AuthenticationManager authenticationManager,
public AuthenticationProcessInterceptor(AuthenticationManager authenticationManager,
GrpcAuthenticationExtractor extractor, AuthorizationManager<CallContext> authorizationManager) {
this.authenticationManager = authenticationManager;
this.extractor = extractor;
@@ -72,56 +71,26 @@ public class AuthenticationServerInterceptor implements ServerInterceptor, Order
}
if (this.authorizationManager != null) {
CallContext context = new CallContext(headers, call.getAttributes(), call.getMethodDescriptor());
if (user == null) {
// Maybe just throw BadCredentialsException (authentication manager would
// have to make the anonymous user)?
user = new AnonymousAuthenticationToken("anonymous", "anonymous",
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
}
return new AuthenticatedListener<ReqT>(next.startCall(call, headers), this.authorizationManager,
new CallContext(headers, call.getAttributes(), call.getMethodDescriptor()), user);
}
return new AuthenticatedListener<ReqT>(next.startCall(call, headers), null,
new CallContext(headers, call.getAttributes(), call.getMethodDescriptor()), user);
}
static class AuthenticatedListener<ReqT> extends ForwardingServerCallListener<ReqT> {
private final Listener<ReqT> delegate;
private final CallContext context;
private final Authentication authentication;
private final AuthorizationManager<CallContext> authorizationManager;
AuthenticatedListener(io.grpc.ServerCall.Listener<ReqT> delegate,
AuthorizationManager<CallContext> authorizationManager, CallContext context, Authentication user) {
this.delegate = delegate;
this.authorizationManager = authorizationManager;
this.context = context;
this.authentication = user;
}
@Override
public void onReady() {
if (this.authentication == null || !this.authentication.isAuthenticated()) {
throw new BadCredentialsException("not authenticated");
}
else {
if (!this.authorizationManager.authorize(() -> this.authentication, this.context).isGranted()) {
if (this.authentication instanceof AnonymousAuthenticationToken) {
throw new BadCredentialsException("not authenticated");
}
throw new AccessDeniedException("not allowed");
Authentication authentication = user;
if (!this.authorizationManager.authorize(() -> authentication, context).isGranted()) {
if (user instanceof AnonymousAuthenticationToken) {
throw new BadCredentialsException("not authenticated");
}
throw new AccessDeniedException("not allowed");
}
super.onReady();
}
@Override
protected Listener<ReqT> delegate() {
return this.delegate;
}
else if (user == null || !user.isAuthenticated()) {
throw new BadCredentialsException("not authenticated");
}
return next.startCall(call, headers);
}
}

View File

@@ -52,7 +52,7 @@ import io.micrometer.observation.ObservationRegistry;
* @author Dave Syer
*/
public final class GrpcSecurity
extends AbstractConfiguredSecurityBuilder<AuthenticationServerInterceptor, GrpcSecurity> {
extends AbstractConfiguredSecurityBuilder<AuthenticationProcessInterceptor, GrpcSecurity> {
/**
* A constant key used for storing and retrieving the "Authorization" header from gRPC
@@ -100,7 +100,7 @@ public final class GrpcSecurity
}
@Override
protected AuthenticationServerInterceptor performBuild() throws Exception {
protected AuthenticationProcessInterceptor performBuild() throws Exception {
if (this.authenticationManager != null) {
setSharedObject(AuthenticationManager.class, this.authenticationManager);
}
@@ -115,7 +115,7 @@ public final class GrpcSecurity
}
}
this.authenticationExtractors.sort(AnnotationAwareOrderComparator.INSTANCE);
return new AuthenticationServerInterceptor(getSharedObject(AuthenticationManager.class),
return new AuthenticationProcessInterceptor(getSharedObject(AuthenticationManager.class),
new CompositeAuthenticationExtractor(this.authenticationExtractors), this.authorizationManager);
}
@@ -147,7 +147,7 @@ public final class GrpcSecurity
}
@SuppressWarnings({ "unchecked", "removal" })
private <C extends SecurityConfigurerAdapter<AuthenticationServerInterceptor, GrpcSecurity>> C getOrApply(
private <C extends SecurityConfigurerAdapter<AuthenticationProcessInterceptor, GrpcSecurity>> C getOrApply(
C configurer) throws Exception {
C existingConfig = (C) getConfigurer(configurer.getClass());
if (existingConfig != null) {

View File

@@ -22,8 +22,8 @@ import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.userdetails.UserDetailsService;
public final class HttpBasicConfigurer<H extends SecurityBuilder<AuthenticationServerInterceptor>>
extends SecurityConfigurerAdapter<AuthenticationServerInterceptor, H> {
public final class HttpBasicConfigurer<H extends SecurityBuilder<AuthenticationProcessInterceptor>>
extends SecurityConfigurerAdapter<AuthenticationProcessInterceptor, H> {
private final ApplicationContext context;

View File

@@ -24,8 +24,8 @@ import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
public final class PreAuthConfigurer<H extends SecurityBuilder<AuthenticationServerInterceptor>>
extends SecurityConfigurerAdapter<AuthenticationServerInterceptor, H> {
public final class PreAuthConfigurer<H extends SecurityBuilder<AuthenticationProcessInterceptor>>
extends SecurityConfigurerAdapter<AuthenticationProcessInterceptor, H> {
private final ApplicationContext context;

View File

@@ -35,7 +35,7 @@ import org.springframework.util.Assert;
import org.springframework.util.PatternMatchUtils;
import org.springframework.util.function.SingletonSupplier;
public class RequestMapperConfigurer extends SecurityConfigurerAdapter<AuthenticationServerInterceptor, GrpcSecurity> {
public class RequestMapperConfigurer extends SecurityConfigurerAdapter<AuthenticationProcessInterceptor, GrpcSecurity> {
private List<AuthorizedCall> authorizedCalls = new ArrayList<>();
@@ -46,12 +46,12 @@ public class RequestMapperConfigurer extends SecurityConfigurerAdapter<Authentic
public RequestMapperConfigurer(ApplicationContext context) throws Exception {
if (context.getBeanNamesForType(AuthorizationEventPublisher.class).length > 0) {
this.publisher = context.getBean(AuthorizationEventPublisher.class);
} else {
}
else {
this.publisher = new SpringAuthorizationEventPublisher(context);
}
this.roleHierarchy = SingletonSupplier.of(() -> (context.getBeanNamesForType(RoleHierarchy.class).length > 0)
? context.getBean(RoleHierarchy.class)
: new NullRoleHierarchy());
? context.getBean(RoleHierarchy.class) : new NullRoleHierarchy());
}
@Override
@@ -150,6 +150,7 @@ public class RequestMapperConfigurer extends SecurityConfigurerAdapter<Authentic
public static class RequestMapperAuthorizationManager implements AuthorizationManager<CallContext> {
private final List<AuthorizedCall> authorizedCalls;
private final AuthorizationEventPublisher publisher;
public RequestMapperAuthorizationManager(List<AuthorizedCall> authorizedCalls,