All Downloads are FREE. Search and download functionalities are using the official Maven repository.

br.com.anteros.security.spring.config.AbstractSpringGlobalMethodSecurityConfiguration Maven / Gradle / Ivy

There is a newer version: 2.0.20
Show newest version
package br.com.anteros.security.spring.config;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.aopalliance.intercept.MethodInterceptor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.AfterInvocationProvider;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
import org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory;
import org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice;
import org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.intercept.AfterInvocationManager;
import org.springframework.security.access.intercept.AfterInvocationProviderManager;
import org.springframework.security.access.intercept.RunAsManager;
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;
import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;
import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource;
import org.springframework.security.access.method.MethodSecurityMetadataSource;
import org.springframework.security.access.prepost.PostInvocationAdviceProvider;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;
import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.util.Assert;

import br.com.anteros.security.spring.AnterosSecurityAccessDecisionManager;
import br.com.anteros.security.spring.AnterosSecurityVoter;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public abstract class AbstractSpringGlobalMethodSecurityConfiguration
		implements ImportAware, SmartInitializingSingleton {
	private static final Log logger = LogFactory.getLog(AbstractSpringGlobalMethodSecurityConfiguration.class);
	private ObjectPostProcessor objectPostProcessor = new ObjectPostProcessor() {
		public  T postProcess(T object) {
			throw new IllegalStateException(ObjectPostProcessor.class.getName()
					+ " is a required bean. Ensure you have used @" + EnableGlobalMethodSecurity.class.getName());
		}
	};
	private DefaultMethodSecurityExpressionHandler defaultMethodExpressionHandler = new DefaultMethodSecurityExpressionHandler();
	private AuthenticationManager authenticationManager;
	private AuthenticationManagerBuilder auth;
	private boolean disableAuthenticationRegistry;
	private AnnotationAttributes enableMethodSecurity;
	private ApplicationContext context;
	private MethodSecurityExpressionHandler expressionHandler;
	private Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource;
	private MethodSecurityInterceptor methodSecurityInterceptor;

	/**
	 * Creates the default MethodInterceptor which is a MethodSecurityInterceptor
	 * using the following methods to construct it.
	 * 
    *
  • {@link #accessDecisionManager()}
  • *
  • {@link #afterInvocationManager()}
  • *
  • {@link #authenticationManager()}
  • *
  • {@link #methodSecurityMetadataSource()}
  • *
  • {@link #runAsManager()}
  • * *
* *

* Subclasses can override this method to provide a different * {@link MethodInterceptor}. *

* * @return * @throws Exception */ @Bean public MethodInterceptor methodSecurityInterceptor() throws Exception { this.methodSecurityInterceptor = isAspectJ() ? new AspectJMethodSecurityInterceptor() : new MethodSecurityInterceptor(); methodSecurityInterceptor.setAccessDecisionManager(accessDecisionManager()); methodSecurityInterceptor.setAfterInvocationManager(afterInvocationManager()); methodSecurityInterceptor.setSecurityMetadataSource(methodSecurityMetadataSource()); RunAsManager runAsManager = runAsManager(); if (runAsManager != null) { methodSecurityInterceptor.setRunAsManager(runAsManager); } return this.methodSecurityInterceptor; } /* * (non-Javadoc) * * @see org.springframework.beans.factory.SmartInitializingSingleton# * afterSingletonsInstantiated() */ @Override public void afterSingletonsInstantiated() { try { initializeMethodSecurityInterceptor(); } catch (Exception e) { throw new RuntimeException(e); } PermissionEvaluator permissionEvaluator = getSingleBeanOrNull(PermissionEvaluator.class); if (permissionEvaluator != null) { this.defaultMethodExpressionHandler.setPermissionEvaluator(permissionEvaluator); } RoleHierarchy roleHierarchy = getSingleBeanOrNull(RoleHierarchy.class); if (roleHierarchy != null) { this.defaultMethodExpressionHandler.setRoleHierarchy(roleHierarchy); } AuthenticationTrustResolver trustResolver = getSingleBeanOrNull(AuthenticationTrustResolver.class); if (trustResolver != null) { this.defaultMethodExpressionHandler.setTrustResolver(trustResolver); } GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(GrantedAuthorityDefaults.class); if (grantedAuthorityDefaults != null) { this.defaultMethodExpressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix()); } } private T getSingleBeanOrNull(Class type) { String[] beanNamesForType = this.context.getBeanNamesForType(type); if (beanNamesForType == null || beanNamesForType.length != 1) { return null; } return this.context.getBean(beanNamesForType[0], type); } private void initializeMethodSecurityInterceptor() throws Exception { if (this.methodSecurityInterceptor == null) { return; } this.methodSecurityInterceptor.setAuthenticationManager(authenticationManager()); } /** * Provide a custom {@link AfterInvocationManager} for the default * implementation of {@link #methodSecurityInterceptor()}. The default is null * if pre post is not enabled. Otherwise, it returns a * {@link AfterInvocationProviderManager}. * *

* Subclasses should override this method to provide a custom * {@link AfterInvocationManager} *

* * @return */ protected AfterInvocationManager afterInvocationManager() { if (prePostEnabled()) { AfterInvocationProviderManager invocationProviderManager = new AfterInvocationProviderManager(); ExpressionBasedPostInvocationAdvice postAdvice = new ExpressionBasedPostInvocationAdvice( getExpressionHandler()); PostInvocationAdviceProvider postInvocationAdviceProvider = new PostInvocationAdviceProvider(postAdvice); List afterInvocationProviders = new ArrayList<>(); afterInvocationProviders.add(postInvocationAdviceProvider); invocationProviderManager.setProviders(afterInvocationProviders); return invocationProviderManager; } return null; } /** * Provide a custom {@link RunAsManager} for the default implementation of * {@link #methodSecurityInterceptor()}. The default is null. * * @return */ protected RunAsManager runAsManager() { return null; } /** * Allows subclasses to provide a custom {@link AccessDecisionManager}. The * default is a {@link AffirmativeBased} with the following voters: * *
    *
  • {@link PreInvocationAuthorizationAdviceVoter}
  • *
  • {@link RoleVoter}
  • *
  • {@link AuthenticatedVoter}
  • *
* * @return */ protected AccessDecisionManager accessDecisionManager() { List> decisionVoters = new ArrayList>(); decisionVoters.add(new AnterosSecurityVoter()); AnterosSecurityAccessDecisionManager result = new AnterosSecurityAccessDecisionManager(decisionVoters); return result; } /** * Provide a {@link MethodSecurityExpressionHandler} that is registered with the * {@link ExpressionBasedPreInvocationAdvice}. The default is * {@link DefaultMethodSecurityExpressionHandler} which optionally will Autowire * an {@link AuthenticationTrustResolver}. * *

* Subclasses may override this method to provide a custom * {@link MethodSecurityExpressionHandler} *

* * @return */ protected MethodSecurityExpressionHandler createExpressionHandler() { return defaultMethodExpressionHandler; } /** * Gets the {@link MethodSecurityExpressionHandler} or creates it using * {@link #expressionHandler}. * * @return a non {@code null} {@link MethodSecurityExpressionHandler} */ protected final MethodSecurityExpressionHandler getExpressionHandler() { if (expressionHandler == null) { expressionHandler = createExpressionHandler(); } return expressionHandler; } /** * Provides a custom {@link MethodSecurityMetadataSource} that is registered * with the {@link #methodSecurityMetadataSource()}. Default is null. * * @return a custom {@link MethodSecurityMetadataSource} that is registered with * the {@link #methodSecurityMetadataSource()} */ protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() { return null; } /** * Allows providing a custom {@link AuthenticationManager}. The default is to * use any authentication mechanisms registered by * {@link #configure(AuthenticationManagerBuilder)}. If * {@link #configure(AuthenticationManagerBuilder)} was not overridden, then an * {@link AuthenticationManager} is attempted to be autowired by type. * * @return */ protected AuthenticationManager authenticationManager() throws Exception { if (authenticationManager == null) { DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor .postProcess(new DefaultAuthenticationEventPublisher()); auth = new AuthenticationManagerBuilder(objectPostProcessor); auth.authenticationEventPublisher(eventPublisher); configure(auth); if (disableAuthenticationRegistry) { authenticationManager = getAuthenticationConfiguration().getAuthenticationManager(); } else { authenticationManager = auth.build(); } } return authenticationManager; } /** * Sub classes can override this method to register different types of * authentication. If not overridden, * {@link #configure(AuthenticationManagerBuilder)} will attempt to autowire by * type. * * @param auth the {@link AuthenticationManagerBuilder} used to register * different authentication mechanisms for the global method * security. * @throws Exception */ protected void configure(AuthenticationManagerBuilder auth) throws Exception { this.disableAuthenticationRegistry = true; } /** * Provides the default {@link MethodSecurityMetadataSource} that will be used. * It creates a {@link DelegatingMethodSecurityMetadataSource} based upon * {@link #customMethodSecurityMetadataSource()} and the attributes on * {@link EnableGlobalMethodSecurity}. * * @return */ @Bean public MethodSecurityMetadataSource methodSecurityMetadataSource() { List sources = new ArrayList<>(); ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory( getExpressionHandler()); MethodSecurityMetadataSource customMethodSecurityMetadataSource = customMethodSecurityMetadataSource(); if (customMethodSecurityMetadataSource != null) { sources.add(customMethodSecurityMetadataSource); } if (prePostEnabled()) { sources.add(new PrePostAnnotationSecurityMetadataSource(attributeFactory)); } if (securedEnabled()) { sources.add(new SecuredAnnotationSecurityMetadataSource()); } if (jsr250Enabled()) { GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(GrantedAuthorityDefaults.class); if (grantedAuthorityDefaults != null) { this.jsr250MethodSecurityMetadataSource.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix()); } sources.add(jsr250MethodSecurityMetadataSource); } return new DelegatingMethodSecurityMetadataSource(sources); } /** * Creates the {@link PreInvocationAuthorizationAdvice} to be used. The default * is {@link ExpressionBasedPreInvocationAdvice}. * * @return */ @Bean public PreInvocationAuthorizationAdvice preInvocationAuthorizationAdvice() { ExpressionBasedPreInvocationAdvice preInvocationAdvice = new ExpressionBasedPreInvocationAdvice(); preInvocationAdvice.setExpressionHandler(getExpressionHandler()); return preInvocationAdvice; } /** * Obtains the attributes from {@link EnableGlobalMethodSecurity} if this class * was imported using the {@link EnableGlobalMethodSecurity} annotation. */ public final void setImportMetadata(AnnotationMetadata importMetadata) { Map annotationAttributes = importMetadata .getAnnotationAttributes(EnableGlobalMethodSecurity.class.getName()); enableMethodSecurity = AnnotationAttributes.fromMap(annotationAttributes); } @Autowired(required = false) public void setObjectPostProcessor(ObjectPostProcessor objectPostProcessor) { this.objectPostProcessor = objectPostProcessor; this.defaultMethodExpressionHandler = objectPostProcessor.postProcess(defaultMethodExpressionHandler); } @Autowired(required = false) public void setJsr250MethodSecurityMetadataSource( Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource) { this.jsr250MethodSecurityMetadataSource = jsr250MethodSecurityMetadataSource; } @Autowired(required = false) public void setMethodSecurityExpressionHandler(List handlers) { if (handlers.size() != 1) { logger.debug("Not autowiring MethodSecurityExpressionHandler since size != 1. Got " + handlers); return; } this.expressionHandler = handlers.get(0); } @Autowired public void setApplicationContext(ApplicationContext context) { this.context = context; } private AuthenticationConfiguration getAuthenticationConfiguration() { return context.getBean(AuthenticationConfiguration.class); } private boolean prePostEnabled() { return enableMethodSecurity().getBoolean("prePostEnabled"); } private boolean securedEnabled() { return enableMethodSecurity().getBoolean("securedEnabled"); } private boolean jsr250Enabled() { return enableMethodSecurity().getBoolean("jsr250Enabled"); } private int order() { return (Integer) enableMethodSecurity().get("order"); } private boolean isAspectJ() { return enableMethodSecurity().getEnum("mode") == AdviceMode.ASPECTJ; } private AnnotationAttributes enableMethodSecurity() { if (enableMethodSecurity == null) { // if it is null look at this instance (i.e. a subclass was used) EnableGlobalMethodSecurity methodSecurityAnnotation = AnnotationUtils.findAnnotation(getClass(), EnableGlobalMethodSecurity.class); Assert.notNull(methodSecurityAnnotation, EnableGlobalMethodSecurity.class.getName() + " is required"); Map methodSecurityAttrs = AnnotationUtils.getAnnotationAttributes(methodSecurityAnnotation); this.enableMethodSecurity = AnnotationAttributes.fromMap(methodSecurityAttrs); } return this.enableMethodSecurity; } }