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.context.ApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager;
@ -107,6 +108,8 @@ import java.util.List;
*/
public class RSocketSecurity {
private List<PayloadInterceptor> payloadInterceptors = new ArrayList<>();
private BasicAuthenticationSpec basicAuthSpec;
private JwtSpec jwtSpec;
@ -117,6 +120,22 @@ public class RSocketSecurity {
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) {
this.authenticationManager = authenticationManager;
return this;
@ -147,7 +166,9 @@ public class RSocketSecurity {
protected AuthenticationPayloadInterceptor build() {
ReactiveAuthenticationManager manager = getAuthenticationManager();
return new AuthenticationPayloadInterceptor(manager);
AuthenticationPayloadInterceptor result = new AuthenticationPayloadInterceptor(manager);
result.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());
return result;
}
private BasicAuthenticationSpec() {}
@ -185,6 +206,7 @@ public class RSocketSecurity {
ReactiveAuthenticationManager manager = getAuthenticationManager();
AuthenticationPayloadInterceptor result = new AuthenticationPayloadInterceptor(manager);
result.setAuthenticationConverter(new BearerPayloadExchangeConverter());
result.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());
return result;
}
@ -209,20 +231,27 @@ public class RSocketSecurity {
}
private List<PayloadInterceptor> payloadInterceptors() {
List<PayloadInterceptor> payloadInterceptors = new ArrayList<>();
List<PayloadInterceptor> result = new ArrayList<>(this.payloadInterceptors);
if (this.basicAuthSpec != null) {
payloadInterceptors.add(this.basicAuthSpec.build());
result.add(this.basicAuthSpec.build());
}
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) {
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 {
@ -239,7 +268,9 @@ public class RSocketSecurity {
}
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) {

View File

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

View File

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

View File

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