如何将基于方法的安全性添加到Spring Boot项目?

我想为Spring Boot项目添加基于方法的安全性。

似乎我只需要添加PermissionEvaluatorMethodSecurityExpressionHandler bean,使用@EnableGlobalMethodSecurity(prePostEnabled = true)注释我的WebSecurityConfigurerAdapter ,使用@PreAuthorize("isAuthenticated() and hasPermission(#param, 'somePermissionName')")

但是在添加PermissionEvaluator bean之后

 @Bean public PermissionEvaluator permissionEvaluator() { HelloPermissionEvaluator bean = new HelloPermissionEvaluator(); return bean; } 

我得到一个IllegalArgumentException :“需要一个ServletContext来配置默认的servlet处理”:

 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.(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() at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerBySpringCGLIB$$d7f296e3$$FastClassBySpringCGLIB$$48c20692.invoke() 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() 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 

我在网上找到的所有内容都与jUnit测试有关。 为什么抛出这个exception? 我错过了什么? 我是否必须添加ServletContext bean,如果是,如何添加?

我的要求是Gradle,Spring Boot和java配置(而不是XML配置)。 最小和完整的来源如下:


Application.java

 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); } } 

HelloController.java

 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; } } 

HelloPermissionEvaluator.java

 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."); } } 

WebSecurityConfig.java

 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; } } 

的build.gradle

 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' } 

尝试将PermissionEvaluator放在单独的@Configuration类中。 您似乎在ServletContext准备好之前强制它被实例化(必须超级创建Spring Securityfilter,以便这可能发生)。

陷入类似的问题。 如果要在表达式处理程序中配置自定义权限评估程序,可以执行以下操作

 public class SecurityPermissionEvaluator implements PermissionEvaluator { //A is a spring managed bean on which permission evaluator depends private A a; @Autowired public SecurityPermissionEvaluator(A a){ this.a = a; } @Override public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) { return false; } } then @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfiguration extends GlobalMethodSecurityConfiguration { @Autowired private A a; @Override protected MethodSecurityExpressionHandler createExpressionHandler() { DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); PermissionEvaluator permissionEvaluator = new SecurityPermissionEvaluator(a); expressionHandler.setPermissionEvaluator(permissionEvaluator); return expressionHandler; } } 

如果你想明确地使用Permission评估器,那么就像Dave建议的那样,即在其配置文件中定义权限评估器bean。