Question

Is there a way I can disable the global method security using the boolean securityEnabled from my config.properties? Any other approach?

@EnableWebSecurity 
@EnableGlobalMethodSecurity(securedEnabled=true) 
@PropertySource("classpath:config.properties")  
public class SecurityConfig 
  extends WebSecurityConfigurerAdapter {    

  @Value("${securityconfig.enabled}") 
  private boolean securityEnabled;

  ...

}
Was it helpful?

Solution

The easiest way to do this is:

  • Extract method security to its own class
  • Remove the securedEnabled attribute entirely
  • Override the customMethodSecurityMetadataSource method and return the result based on the configured value.

For example:

@EnableWebSecurity
@Configuration
@PropertySource("classpath:config.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
}

@EnableGlobalMethodSecurity
@Configuration
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Value("${securityconfig.enabled}")
    private boolean securityEnabled;

    protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
        return securityEnabled ? new SecuredAnnotationSecurityMetadataSource() : null;
    }    
}

OTHER TIPS

I've managed this by defining a Spring "securityDisabled" profile and conditionally applying security config based off that. I'm using Spring Boot 2.0.2. I believe this should work if not using Spring Boot and in previous versions of Spring Boot, but I have not tested. It's possible some tweaks may be required to property and class names because I know in Spring 2.0 some of that changed.

// In application.properties
spring.profiles.include=securityDisabled

Then my security config looks like this:

@Configuration
public class SecurityConfig {

  // When the securityDisabled profile is applied the following configuration gets used
  @Profile("securityDisabled")
  @EnableWebSecurity
  public class SecurityDisabledConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Configure http as needed from Spring Security defaults when
        // NO security is desired
    }
  }

  // When the securityDisabled profile is NOT applied the following configuration gets used
  @Profile("!securityDisabled")
  @EnableGlobalMethodSecurity(prePostEnabled = true)
  @EnableWebSecurity
  public class SecurityEnabledConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Configure http as needed from Spring Security defaults when
        // security is desired
    }
  }
}

In Springboot2, a simple solution consists in replacing the security method interceptor by a dummy one when the security is off :

@EnableGlobalMethodSecurity(prePostEnabled = true)
static class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Value("${disableSecurity}")
    private boolean disableSecurity;  

    public MethodInterceptor methodSecurityInterceptor(MethodSecurityMetadataSource methodSecurityMetadataSource) {
        return disableSecurity ? new SimpleTraceInterceptor()
                : super.methodSecurityInterceptor(methodSecurityMetadataSource);
    }

}

Thanks to Rob Winch for the solution. For folks who would like to do something similar but with prePostEnabled i have tried and tested the below similar approach and works just fine.

@EnableGlobalMethodSecurity(securedEnabled = true)
@Configuration
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

  @Value("${security.prePostEnabled}")
  private boolean prePostEnabled;

 @Autowired
private DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler;

  protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
     return prePostEnabled ? new PrePostAnnotationSecurityMetadataSource(new ExpressionBasedAnnotationAttributeFactory(defaultMethodSecurityExpressionHandler)) : null ;
}}

EDIT: In addition to above i realized it is required to add following beans to the class. The below will help using the expression based pre invocation checks along with avoiding "ROLE_" prefix that is defaulted in all the handlers

protected AccessDecisionManager accessDecisionManager() {
    AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager();
    ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
    expressionAdvice.setExpressionHandler(getExpressionHandler());
    //This is required in order to allow expression based Voter to allow access
    accessDecisionManager.getDecisionVoters()
            .add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));

    //Remove the ROLE_ prefix from RoleVoter for @Secured and hasRole checks on methods
    accessDecisionManager.getDecisionVoters().stream()
            .filter(RoleVoter.class::isInstance)
            .map(RoleVoter.class::cast)
            .forEach(it -> it.setRolePrefix(""));

    return accessDecisionManager;
}
/**
 * Allow skip ROLE_ when check permission using @PreAuthorize, like:
 * @PreAuthorize("hasAnyRole('USER', 'SYSTEM_ADMIN')")
 * Added all the Beans
 */
@Bean
public DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
    DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
    defaultMethodSecurityExpressionHandler.setDefaultRolePrefix("");
    return defaultMethodSecurityExpressionHandler;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top