title: How do I add method based security to a Spring Boot project?
body: <p>I want to add method based security to a Spring Boot project.</p>
  
  <p>It seemed that all I would need is to add <code>PermissionEvaluator</code> and <code>MethodSecurityExpressionHandler</code> beans, annotate my <code>WebSecurityConfigurerAdapter</code> with <code>@EnableGlobalMethodSecurity(prePostEnabled = true)</code> and the method with <code>@PreAuthorize("isAuthenticated() and hasPermission(#param, 'somePermissionName')")</code>.</p>
  
  <p>But after adding a <code>PermissionEvaluator</code> bean</p>
  
  <pre class="lang-java prettyprint-override"><code>@Bean
  public PermissionEvaluator permissionEvaluator() {
      HelloPermissionEvaluator bean = new HelloPermissionEvaluator();
      return bean;
  }
  </code></pre>
  
  <p>I get an <code>IllegalArgumentException</code>: "A ServletContext is required to configure default servlet handling":</p>
  
  <pre><code>Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultServletHandlerMapping' defined in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.springframework.web.servlet.HandlerMapping org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.defaultServletHandlerMapping()] threw exception; nested exception is java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
      at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:597)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1094)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:989)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
      at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
      at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
      at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
      at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
      at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703)
      at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
      at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:120)
      at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:648)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:311)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:909)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:898)
      at com.domain.simple.Application.main(Application.java:14)
  Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.springframework.web.servlet.HandlerMapping org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.defaultServletHandlerMapping()] threw exception; nested exception is java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
      at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:188)
      at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:586)
      ... 17 more
  Caused by: java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
      at org.springframework.util.Assert.notNull(Assert.java:112)
      at org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer.&lt;init&gt;(DefaultServletHandlerConfigurer.java:54)
      at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.defaultServletHandlerMapping(WebMvcConfigurationSupport.java:346)
      at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerBySpringCGLIB$$d7f296e3.CGLIB$defaultServletHandlerMapping$26(&lt;generated&gt;)
      at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerBySpringCGLIB$$d7f296e3$$FastClassBySpringCGLIB$$48c20692.invoke(&lt;generated&gt;)
      at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
      at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:312)
      at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerBySpringCGLIB$$d7f296e3.defaultServletHandlerMapping(&lt;generated&gt;)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:483)
      at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:166)
      ... 18 more
  </code></pre>
  
  <p>All I could find on the web is related to jUnit testing. Why is this exception being thrown? What am I missing? Do I have to add a ServletContext bean, and if so, how?</p>
  
  <p>My requirements are Gradle, Spring Boot and java config (instead of XML config). The minimal and complete source follows:</p>
  
  <hr>
  
  <p>Application.java</p>
  
  <pre class="lang-java prettyprint-override"><code>package com.domain.simple;
  
  import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  import org.springframework.boot.SpringApplication;
  import org.springframework.context.annotation.ComponentScan;
  import org.springframework.context.annotation.Configuration;
  
  @EnableAutoConfiguration
  @Configuration
  @ComponentScan
  public class Application {
  
      public static void main(String[] args) throws Throwable {
          SpringApplication.run(Application.class, args);
      }
  
  }
  </code></pre>
  
  <p>HelloController.java</p>
  
  <pre class="lang-java prettyprint-override"><code>package com.domain.simple;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.springframework.security.access.prepost.PreAuthorize;
  import org.springframework.stereotype.Controller;
  import org.springframework.web.bind.annotation.PathVariable;
  import org.springframework.web.bind.annotation.RequestMapping;
  import org.springframework.web.bind.annotation.ResponseBody;
  
  @Controller
  public class HelloController {
  
      Logger log = LoggerFactory.getLogger(HelloController.class);
  
      // @PreAuthorize("isAuthenticated() and hasPermission(#param, 'somePermissionName')")
      @RequestMapping(value = "/hello/{param}")
      @ResponseBody
      public String hello(@PathVariable("param") String param) {
          log.info("hello(" + param + ") called");
          return "Hello " + param;
      }
  }
  </code></pre>
  
  <p>HelloPermissionEvaluator.java</p>
  
  <pre class="lang-java prettyprint-override"><code>package com.domain.simple;
  
  import java.io.Serializable;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.springframework.security.access.PermissionEvaluator;
  import org.springframework.security.core.Authentication;
  
  public class HelloPermissionEvaluator implements PermissionEvaluator {
  
      Logger log = LoggerFactory.getLogger(HelloPermissionEvaluator.class);
  
      @Override
      public boolean hasPermission(Authentication authentication,
              Object targetDomainObject, Object permission) {
          log.info("hasPermission(Authentication, Object, Object) called");
          return true;
      }
  
      @Override
      public boolean hasPermission(Authentication authentication,
              Serializable targetId, String targetType, Object permission) {
          log.error("hasPermission(Authentication, Serializable, String, Object) called");
          throw new RuntimeException("ID based permission evaluation currently not supported.");
      }
  }
  </code></pre>
  
  <p>WebSecurityConfig.java</p>
  
  <pre class="lang-java prettyprint-override"><code>package com.domain.simple;
  
  import org.springframework.context.annotation.Bean;
  import org.springframework.context.annotation.ComponentScan;
  import org.springframework.context.annotation.Configuration;
  import org.springframework.security.access.PermissionEvaluator;
  import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
  import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
  import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
  import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  
  @Configuration
  @ComponentScan
  @EnableWebSecurity
  @EnableGlobalMethodSecurity(prePostEnabled = true)
  public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  
      @Override
      protected void configure(AuthenticationManagerBuilder authManagerBuilder)
              throws Exception {
          authManagerBuilder.inMemoryAuthentication().withUser("user")
                  .password("password").roles("USER");
      }
  
  //    @Bean
  //    public MethodSecurityExpressionHandler expressionHandler() {
  //        DefaultMethodSecurityExpressionHandler bean = new DefaultMethodSecurityExpressionHandler();
  //        bean.setPermissionEvaluator(permissionEvaluator());
  //        return bean;
  //    }
  
      // this causes an IllegalArgumentException ("A ServletContext is required to configure default servlet handling")
      @Bean
      public PermissionEvaluator permissionEvaluator() {
          HelloPermissionEvaluator bean = new HelloPermissionEvaluator();
          return bean;
      }
  
  }
  </code></pre>
  
  <p>build.gradle</p>
  
  <pre class="lang-groovy prettyprint-override"><code>buildscript {
      repositories {
          maven { url "http://repo.spring.io/libs-snapshot" }
          mavenLocal()
      }
      dependencies {
          classpath("org.springframework.boot:spring-boot-gradle-plugin:1.0.2.RELEASE")
      }
  }
  
  apply plugin: 'eclipse'
  apply plugin: 'java'
  apply plugin: 'spring-boot'
  
  jar {
      baseName = 'simple'
      version =  '0.1.0'
  }
  
  repositories {
      mavenCentral()
      maven { url "http://repo.spring.io/libs-snapshot" }
  }
  
  dependencies {
      compile("org.springframework.boot:spring-boot-starter-web")
      compile("org.springframework.boot:spring-boot-starter-security")
  }
  
  task wrapper(type: Wrapper) {
      gradleVersion = '1.12'
  }
  </code></pre>