INT-3331: Add ChannelSecurityInterceptorFB

JIRA: https://jira.spring.io/browse/INT-3331

INT-3331: PR comments and others

* Register `ChannelSecurityInterceptorBeanPostProcessor` as a `BeanDefinition` (not `BPP`)
* Get `ChannelSecurityInterceptor`s from `ChannelSecurityInterceptorBeanPostProcessor#afterPropertiesSet()`
* Make `ChannelSecurityInterceptor` `final` to disallow to subclass it for unexpected issues
* Provide more convenience to the `ChannelSecurityInterceptorFactoryBean` - to allow to use it from xml configuration

Doc Polishing
This commit is contained in:
Artem Bilan
2014-03-20 11:58:22 +02:00
committed by Gary Russell
parent 5587d79070
commit e3f8ef534b
12 changed files with 425 additions and 34 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2014 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.
@@ -24,14 +24,14 @@ import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.util.Assert;
/**
* An AOP interceptor that enforces authorization for MessageChannel send and/or receive calls.
*
*
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class ChannelSecurityInterceptor extends AbstractSecurityInterceptor implements MethodInterceptor {
public final class ChannelSecurityInterceptor extends AbstractSecurityInterceptor implements MethodInterceptor {
private final ChannelSecurityMetadataSource securityMetadataSource;

View File

@@ -25,10 +25,15 @@ import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.integration.security.channel.ChannelSecurityInterceptor;
import org.springframework.integration.security.channel.ChannelSecurityMetadataSource;
import org.springframework.messaging.MessageChannel;
import org.springframework.util.Assert;
/**
* A {@link BeanPostProcessor} that proxies {@link MessageChannel}s to apply a {@link ChannelSecurityInterceptor}.
@@ -37,13 +42,21 @@ import org.springframework.messaging.MessageChannel;
* @author Oleg Zhurakousky
* @author Artem Bilan
*/
public class ChannelSecurityInterceptorBeanPostProcessor implements BeanPostProcessor {
public class ChannelSecurityInterceptorBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware, InitializingBean {
private final Collection<ChannelSecurityInterceptor> securityInterceptors;
private volatile Collection<ChannelSecurityInterceptor> securityInterceptors;
private ListableBeanFactory beanFactory;
public ChannelSecurityInterceptorBeanPostProcessor(Collection<ChannelSecurityInterceptor> securityInterceptors) {
this.securityInterceptors = securityInterceptors;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
Assert.isInstanceOf(ListableBeanFactory.class, beanFactory);
this.beanFactory = (ListableBeanFactory) beanFactory;
}
@Override
public void afterPropertiesSet() throws Exception {
this.securityInterceptors = this.beanFactory.getBeansOfType(ChannelSecurityInterceptor.class).values();
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
@@ -79,5 +92,4 @@ public class ChannelSecurityInterceptorBeanPostProcessor implements BeanPostProc
}
return false;
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright 2014 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
*
* http://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.integration.security.config;
import java.util.Map;
import java.util.regex.Pattern;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.integration.security.channel.ChannelSecurityInterceptor;
import org.springframework.integration.security.channel.ChannelSecurityMetadataSource;
import org.springframework.integration.security.channel.DefaultChannelAccessPolicy;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.intercept.AfterInvocationManager;
import org.springframework.security.access.intercept.RunAsManager;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.util.Assert;
/**
* The {@link FactoryBean} for {@code <security:secured-channels/>} JavaConfig variant to provide options
* for {@link ChannelSecurityInterceptor} beans.
*
* @author Artem Bilan
* @since 4.0
*/
public class ChannelSecurityInterceptorFactoryBean implements FactoryBean<ChannelSecurityInterceptor>, BeanNameAware, BeanFactoryAware {
private final ChannelSecurityInterceptor interceptor = new ChannelSecurityInterceptor(new ChannelSecurityMetadataSource());
private BeanFactory beanFactory;
private String name;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
if (this.interceptor.getAuthenticationManager() == null && beanFactory.containsBean("authenticationManager")) {
this.interceptor.setAuthenticationManager(beanFactory.getBean("authenticationManager", AuthenticationManager.class));
}
if (this.interceptor.getAccessDecisionManager() == null && beanFactory.containsBean("accessDecisionManager")) {
this.interceptor.setAccessDecisionManager(beanFactory.getBean("accessDecisionManager", AccessDecisionManager.class));
}
}
@Override
public void setBeanName(String name) {
this.name = name;
}
public ChannelSecurityInterceptorFactoryBean setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
interceptor.setAccessDecisionManager(accessDecisionManager);
return this;
}
public ChannelSecurityInterceptorFactoryBean setAfterInvocationManager(AfterInvocationManager afterInvocationManager) {
interceptor.setAfterInvocationManager(afterInvocationManager);
return this;
}
public ChannelSecurityInterceptorFactoryBean setAlwaysReauthenticate(boolean alwaysReauthenticate) {
interceptor.setAlwaysReauthenticate(alwaysReauthenticate);
return this;
}
public ChannelSecurityInterceptorFactoryBean setAuthenticationManager(AuthenticationManager newManager) {
interceptor.setAuthenticationManager(newManager);
return this;
}
public ChannelSecurityInterceptorFactoryBean setPublishAuthorizationSuccess(boolean publishAuthorizationSuccess) {
interceptor.setPublishAuthorizationSuccess(publishAuthorizationSuccess);
return this;
}
public ChannelSecurityInterceptorFactoryBean setRejectPublicInvocations(boolean rejectPublicInvocations) {
interceptor.setRejectPublicInvocations(rejectPublicInvocations);
return this;
}
public ChannelSecurityInterceptorFactoryBean setRunAsManager(RunAsManager runAsManager) {
interceptor.setRunAsManager(runAsManager);
return this;
}
public ChannelSecurityInterceptorFactoryBean setValidateConfigAttributes(boolean validateConfigAttributes) {
interceptor.setValidateConfigAttributes(validateConfigAttributes);
return this;
}
public ChannelSecurityInterceptorFactoryBean accessPolicy(String pattern, String sendAccess) {
return this.accessPolicy(pattern, sendAccess, null);
}
public ChannelSecurityInterceptorFactoryBean accessPolicy(String pattern, String sendAccess, String receiveAccess) {
Assert.hasText(pattern);
((ChannelSecurityMetadataSource) interceptor.obtainSecurityMetadataSource())
.addPatternMapping(Pattern.compile(pattern), new DefaultChannelAccessPolicy(sendAccess, receiveAccess));
return this;
}
public ChannelSecurityInterceptorFactoryBean setAccessPolicies(Map<String, DefaultChannelAccessPolicy> accessPolicies) {
Assert.notNull(accessPolicies);
ChannelSecurityMetadataSource channelSecurityMetadataSource = (ChannelSecurityMetadataSource) interceptor.obtainSecurityMetadataSource();
for (Map.Entry<String, DefaultChannelAccessPolicy> entry : accessPolicies.entrySet()) {
channelSecurityMetadataSource.addPatternMapping(Pattern.compile(entry.getKey()), entry.getValue());
}
return this;
}
@Override
public ChannelSecurityInterceptor getObject() throws Exception {
((AutowireCapableBeanFactory) this.beanFactory).initializeBean(this.interceptor, this.name);
return this.interceptor;
}
@Override
public Class<?> getObjectType() {
return ChannelSecurityInterceptor.class;
}
@Override
public boolean isSingleton() {
return true;
}
}

View File

@@ -17,7 +17,6 @@
package org.springframework.integration.security.config;
import java.util.List;
import java.util.regex.Pattern;
import org.w3c.dom.Element;
@@ -65,9 +64,8 @@ public class SecuredChannelsParser extends AbstractSingleBeanDefinitionParser {
private BeanDefinition parseSecurityMetadataSource(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ChannelSecurityMetadataSource.class);
List<Element> accessPolicyElements = DomUtils.getChildElementsByTagName(element, "access-policy");
ManagedMap<Pattern, BeanDefinition> patternMappings = new ManagedMap<Pattern, BeanDefinition>();
ManagedMap<String, BeanDefinition> patternMappings = new ManagedMap<String, BeanDefinition>();
for (Element accessPolicyElement : accessPolicyElements) {
Pattern pattern = Pattern.compile(accessPolicyElement.getAttribute("pattern"));
String sendAccess = accessPolicyElement.getAttribute("send-access");
String receiveAccess = accessPolicyElement.getAttribute("receive-access");
if (!StringUtils.hasText(sendAccess) && !StringUtils.hasText(receiveAccess)) {
@@ -78,7 +76,7 @@ public class SecuredChannelsParser extends AbstractSingleBeanDefinitionParser {
accessPolicyBuilder.addConstructorArgValue(sendAccess);
accessPolicyBuilder.addConstructorArgValue(receiveAccess);
accessPolicyBuilder.getBeanDefinition().setRole(BeanDefinition.ROLE_SUPPORT);
patternMappings.put(pattern, accessPolicyBuilder.getBeanDefinition());
patternMappings.put(accessPolicyElement.getAttribute("pattern"), accessPolicyBuilder.getBeanDefinition());
}
builder.addConstructorArgValue(patternMappings);

View File

@@ -16,12 +16,11 @@
package org.springframework.integration.security.config;
import java.util.Collection;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.integration.config.IntegrationConfigurationInitializer;
import org.springframework.integration.security.channel.ChannelSecurityInterceptor;
/**
* The Integration Security infrastructure {@code beanFactory} initializer.
@@ -31,10 +30,14 @@ import org.springframework.integration.security.channel.ChannelSecurityIntercept
*/
public class SecurityIntegrationConfigurationInitializer implements IntegrationConfigurationInitializer {
private static final String CHANNEL_SECURITY_INTERCEPTOR_BPP_BEAN_NAME = ChannelSecurityInterceptorBeanPostProcessor.class.getName();
@Override
public void initialize(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Collection<ChannelSecurityInterceptor> securityInterceptors = beanFactory.getBeansOfType(ChannelSecurityInterceptor.class).values();
beanFactory.addBeanPostProcessor(new ChannelSecurityInterceptorBeanPostProcessor(securityInterceptors));
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
if (!registry.containsBeanDefinition(CHANNEL_SECURITY_INTERCEPTOR_BPP_BEAN_NAME)) {
registry.registerBeanDefinition(CHANNEL_SECURITY_INTERCEPTOR_BPP_BEAN_NAME, new RootBeanDefinition(ChannelSecurityInterceptorBeanPostProcessor.class));
}
}
}