Ich möchte einem Spring Boot-Projekt methodenbasierte Sicherheit hinzufügen.
Es schien, als würde ich nur PermissionEvaluator
und MethodSecurityExpressionHandler
Beans hinzufügen, meine WebSecurityConfigurerAdapter
mit @EnableGlobalMethodSecurity(prePostEnabled = true)
und die Methode mit @PreAuthorize("isAuthenticated() and hasPermission(#param, 'somePermissionName')")
kommentieren müssen.
Aber nach dem Hinzufügen einer PermissionEvaluator
Bean
@Bean
public PermissionEvaluator permissionEvaluator() {
HelloPermissionEvaluator bean = new HelloPermissionEvaluator();
return bean;
}
Ich erhalte eine IllegalArgumentException
: "Ein ServletContext ist erforderlich, um die Standard-Servlet-Behandlung zu konfigurieren":
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
Alles, was ich im Web finden konnte, bezieht sich auf jUnit-Tests. Warum wird diese Ausnahme ausgelöst? Was vermisse ich? Muss ich eine ServletContext-Bean hinzufügen und wenn ja, wie?
Meine Anforderungen sind Gradle, Spring Boot und Java Config (anstelle von XML Config). Die minimale und vollständige Quelle folgt:
Anwendung.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'
}
Versuchen Sie, die Variable PermissionEvaluator
in einer separaten Klasse @Configuration
zu platzieren. Sie scheinen es zu zwingen, es zu instanziieren, bevor die ServletContext
fertig ist (Spring Security-Filter müssen sehr früh erstellt werden, damit dies passieren kann).
Ich bin in eine ähnliche Ausgabe geraten. Wenn Sie ein benutzerdefiniertes Berechtigungsauswertungsprogramm im Ausdruckshandler konfigurieren möchten, können Sie Folgendes tun
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;
}
}
wenn Sie den Permission-Evaluator explizit verwenden möchten, tun Sie dies zusammen mit den obigen Anweisungen, wie von Dave vorgeschlagen, dh definieren Sie die Berechtigungs-Evaluator-Bean in ihrer on-Konfigurationsdatei.