Allow Custom PayloadInterceptor to be Added

Fixes gh-7362
This commit is contained in:
Rob Winch 2019-09-06 14:52:47 -05:00
parent ee0d7f6a79
commit 316380e622
5 changed files with 143 additions and 11 deletions

View File

@ -0,0 +1,67 @@
/*
* Copyright 2019 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.security.config.annotation.rsocket;
import org.springframework.core.Ordered;
import org.springframework.security.config.Customizer;
/**
* The standard order for {@link org.springframework.security.rsocket.PayloadInterceptor} to be
* sorted. The actual values might change, so users should use the {@link #getOrder()} method to
* calculate the position dynamically rather than copy values.
*
* @author Rob Winch
* @since 5.2
*/
public enum PayloadInterceptorOrder implements Ordered {
/**
* Where basic authentication is placed.
* @see RSocketSecurity#basicAuthentication(Customizer)
*/
BASIC_AUTHENTICATION,
/**
* Where JWT based authentication is performed.
* @see RSocketSecurity#jwt(Customizer)
*/
JWT_AUTHENTICATION,
/**
* A generic placeholder for other types of authentication.
* @see org.springframework.security.rsocket.authentication.AuthenticationPayloadInterceptor
*/
AUTHENTICATION,
/**
* Where anonymous authentication is placed.
*/
ANONYMOUS,
/**
* Where authorization is placed.
* @see org.springframework.security.rsocket.authorization.AuthorizationPayloadInterceptor
*/
AUTHORIZATION;
private static final int INTERVAL = 100;
private int order;
PayloadInterceptorOrder() {
this.order = ordinal() * INTERVAL;
}
public int getOrder() {
return this.order;
}
}

View File

@ -19,6 +19,7 @@ package org.springframework.security.config.annotation.rsocket;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler; import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
import org.springframework.security.authentication.ReactiveAuthenticationManager; import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager; import org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager;
@ -107,6 +108,8 @@ import java.util.List;
*/ */
public class RSocketSecurity { public class RSocketSecurity {
private List<PayloadInterceptor> payloadInterceptors = new ArrayList<>();
private BasicAuthenticationSpec basicAuthSpec; private BasicAuthenticationSpec basicAuthSpec;
private JwtSpec jwtSpec; private JwtSpec jwtSpec;
@ -117,6 +120,22 @@ public class RSocketSecurity {
private ReactiveAuthenticationManager authenticationManager; private ReactiveAuthenticationManager authenticationManager;
/**
* Adds a {@link PayloadInterceptor} to be used. This is typically only used
* when using the DSL does not meet a users needs. In order to ensure the
* {@link PayloadInterceptor} is done in the proper order the {@link PayloadInterceptor} should
* either implement {@link org.springframework.core.Ordered} or be annotated with
* {@link org.springframework.core.annotation.Order}.
*
* @param interceptor
* @return the builder for additional customizations
* @see PayloadInterceptorOrder
*/
public RSocketSecurity addPayloadInterceptor(PayloadInterceptor interceptor) {
this.payloadInterceptors.add(interceptor);
return this;
}
public RSocketSecurity authenticationManager(ReactiveAuthenticationManager authenticationManager) { public RSocketSecurity authenticationManager(ReactiveAuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager; this.authenticationManager = authenticationManager;
return this; return this;
@ -147,7 +166,9 @@ public class RSocketSecurity {
protected AuthenticationPayloadInterceptor build() { protected AuthenticationPayloadInterceptor build() {
ReactiveAuthenticationManager manager = getAuthenticationManager(); ReactiveAuthenticationManager manager = getAuthenticationManager();
return new AuthenticationPayloadInterceptor(manager); AuthenticationPayloadInterceptor result = new AuthenticationPayloadInterceptor(manager);
result.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());
return result;
} }
private BasicAuthenticationSpec() {} private BasicAuthenticationSpec() {}
@ -185,6 +206,7 @@ public class RSocketSecurity {
ReactiveAuthenticationManager manager = getAuthenticationManager(); ReactiveAuthenticationManager manager = getAuthenticationManager();
AuthenticationPayloadInterceptor result = new AuthenticationPayloadInterceptor(manager); AuthenticationPayloadInterceptor result = new AuthenticationPayloadInterceptor(manager);
result.setAuthenticationConverter(new BearerPayloadExchangeConverter()); result.setAuthenticationConverter(new BearerPayloadExchangeConverter());
result.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());
return result; return result;
} }
@ -209,20 +231,27 @@ public class RSocketSecurity {
} }
private List<PayloadInterceptor> payloadInterceptors() { private List<PayloadInterceptor> payloadInterceptors() {
List<PayloadInterceptor> payloadInterceptors = new ArrayList<>(); List<PayloadInterceptor> result = new ArrayList<>(this.payloadInterceptors);
if (this.basicAuthSpec != null) { if (this.basicAuthSpec != null) {
payloadInterceptors.add(this.basicAuthSpec.build()); result.add(this.basicAuthSpec.build());
} }
if (this.jwtSpec != null) { if (this.jwtSpec != null) {
payloadInterceptors.add(this.jwtSpec.build()); result.add(this.jwtSpec.build());
} }
payloadInterceptors.add(new AnonymousPayloadInterceptor("anonymousUser")); result.add(anonymous());
if (this.authorizePayload != null) { if (this.authorizePayload != null) {
payloadInterceptors.add(this.authorizePayload.build()); result.add(this.authorizePayload.build());
} }
return payloadInterceptors; AnnotationAwareOrderComparator.sort(result);
return result;
}
private AnonymousPayloadInterceptor anonymous() {
AnonymousPayloadInterceptor result = new AnonymousPayloadInterceptor("anonymousUser");
result.setOrder(PayloadInterceptorOrder.ANONYMOUS.getOrder());
return result;
} }
public class AuthorizePayloadsSpec { public class AuthorizePayloadsSpec {
@ -239,7 +268,9 @@ public class RSocketSecurity {
} }
protected AuthorizationPayloadInterceptor build() { protected AuthorizationPayloadInterceptor build() {
return new AuthorizationPayloadInterceptor(this.authzBuilder.build()); AuthorizationPayloadInterceptor result = new AuthorizationPayloadInterceptor(this.authzBuilder.build());
result.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());
return result;
} }
public Access route(String pattern) { public Access route(String pattern) {

View File

@ -16,6 +16,7 @@
package org.springframework.security.rsocket.authentication; package org.springframework.security.rsocket.authentication;
import org.springframework.core.Ordered;
import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.AuthorityUtils;
@ -35,12 +36,13 @@ import java.util.List;
* @author Rob Winch * @author Rob Winch
* @since 5.2 * @since 5.2
*/ */
public class AnonymousPayloadInterceptor implements PayloadInterceptor { public class AnonymousPayloadInterceptor implements PayloadInterceptor, Ordered {
private String key; private String key;
private Object principal; private Object principal;
private List<GrantedAuthority> authorities; private List<GrantedAuthority> authorities;
private int order;
/** /**
* Creates a filter with a principal named "anonymousUser" and the single authority * Creates a filter with a principal named "anonymousUser" and the single authority
@ -67,6 +69,14 @@ public class AnonymousPayloadInterceptor implements PayloadInterceptor {
this.authorities = authorities; this.authorities = authorities;
} }
@Override
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
@Override @Override
public Mono<Void> intercept(PayloadExchange exchange, PayloadInterceptorChain chain) { public Mono<Void> intercept(PayloadExchange exchange, PayloadInterceptorChain chain) {

View File

@ -16,6 +16,7 @@
package org.springframework.security.rsocket.authentication; package org.springframework.security.rsocket.authentication;
import org.springframework.core.Ordered;
import org.springframework.security.authentication.ReactiveAuthenticationManager; import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.ReactiveSecurityContextHolder;
@ -33,10 +34,12 @@ import reactor.core.publisher.Mono;
* @author Rob Winch * @author Rob Winch
* @since 5.2 * @since 5.2
*/ */
public class AuthenticationPayloadInterceptor implements PayloadInterceptor { public class AuthenticationPayloadInterceptor implements PayloadInterceptor, Ordered {
private final ReactiveAuthenticationManager authenticationManager; private final ReactiveAuthenticationManager authenticationManager;
private int order;
private PayloadExchangeAuthenticationConverter authenticationConverter = private PayloadExchangeAuthenticationConverter authenticationConverter =
new BasicAuthenticationPayloadExchangeConverter(); new BasicAuthenticationPayloadExchangeConverter();
@ -49,6 +52,15 @@ public class AuthenticationPayloadInterceptor implements PayloadInterceptor {
this.authenticationManager = authenticationManager; this.authenticationManager = authenticationManager;
} }
@Override
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
/** /**
* Sets the convert to be used * Sets the convert to be used
* @param authenticationConverter * @param authenticationConverter

View File

@ -16,6 +16,7 @@
package org.springframework.security.rsocket.authorization; package org.springframework.security.rsocket.authorization;
import org.springframework.core.Ordered;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authorization.ReactiveAuthorizationManager; import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.ReactiveSecurityContextHolder;
@ -32,15 +33,26 @@ import org.springframework.security.rsocket.PayloadInterceptor;
* @author Rob Winch * @author Rob Winch
* @since 5.2 * @since 5.2
*/ */
public class AuthorizationPayloadInterceptor implements PayloadInterceptor { public class AuthorizationPayloadInterceptor implements PayloadInterceptor, Ordered {
private final ReactiveAuthorizationManager<PayloadExchange> authorizationManager; private final ReactiveAuthorizationManager<PayloadExchange> authorizationManager;
private int order;
public AuthorizationPayloadInterceptor( public AuthorizationPayloadInterceptor(
ReactiveAuthorizationManager<PayloadExchange> authorizationManager) { ReactiveAuthorizationManager<PayloadExchange> authorizationManager) {
Assert.notNull(authorizationManager, "authorizationManager cannot be null"); Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.authorizationManager = authorizationManager; this.authorizationManager = authorizationManager;
} }
@Override
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
@Override @Override
public Mono<Void> intercept(PayloadExchange exchange, PayloadInterceptorChain chain) { public Mono<Void> intercept(PayloadExchange exchange, PayloadInterceptorChain chain) {
return ReactiveSecurityContextHolder.getContext() return ReactiveSecurityContextHolder.getContext()