web-dev-qa-db-de.com

Wie füge ich einem Spring Boot-Projekt methodenbasierte Sicherheit hinzu?

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

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).

31
Dave Syer

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.

0
Anil Punia