我想在Spring Boot项目中添加基于方法的安全性。
似乎我只需要添加PermissionEvaluator和MethodSecurityExpressionHandlerbean,WebSecurityConfigurerAdapter用@EnableGlobalMethodSecurity(prePostEnabled = true)和注释方法@PreAuthorize("isAuthenticated() and hasPermission(#param, 'somePermissionName')")。
PermissionEvaluator
MethodSecurityExpressionHandlerbean,WebSecurityConfigurerAdapter
@EnableGlobalMethodSecurity(prePostEnabled = true)
@PreAuthorize("isAuthenticated() and hasPermission(#param, 'somePermissionName')")
但是加入PermissionEvaluator豆子之后
@Bean public PermissionEvaluator permissionEvaluator() { HelloPermissionEvaluator bean = new HelloPermissionEvaluator(); return bean; }
我得到一个消息IllegalArgumentException:“配置默认的servlet处理需要ServletContext”:
IllegalArgumentException
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.<init>(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(<generated>) at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerBySpringCGLIB$$d7f296e3$$FastClassBySpringCGLIB$$48c20692.invoke(<generated>) 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(<generated>) 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测试有关。为什么会引发此异常?我想念什么?我是否必须添加一个ServletContext bean,如果是,如何添加?
我的要求是Gradle,Spring Boot和java config(而不是XML config)。最小和完整的来源如下:
应用程序
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 Security过滤器,这样才可以发生)。
@Configuration
ServletContext