tutorial example español application java spring spring-security spring-boot

java - example - spring boot wikipedia



¿Cómo agrego la seguridad basada en el método a un proyecto Spring Boot? (2)

Quiero agregar seguridad basada en métodos a un proyecto Spring Boot.

Parecía que todo lo que necesitaría es agregar PermissionEvaluator y MethodSecurityExpressionHandler beans, anotar mi WebSecurityConfigurerAdapter con @EnableGlobalMethodSecurity(prePostEnabled = true) el que se @PreAuthorize("isAuthenticated() and hasPermission(#param, ''somePermissionName'')") .

Pero después de agregar un bean PermissionEvaluator

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

Obtengo una IllegalArgumentException : "Se requiere un ServletContext para configurar el manejo de servlet predeterminado":

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

Todo lo que pude encontrar en la web está relacionado con las pruebas de jUnit. ¿Por qué se lanza esta excepción? ¿Qué me estoy perdiendo? ¿Tengo que agregar un bean ServletContext, y si es así, cómo?

Mis requisitos son Gradle, Spring Boot y java config (en lugar de XML config). La fuente mínima y completa sigue:

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

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


Intente colocar el PermissionEvaluator en una clase @Configuration separada. Parece que estás obligando a que se cree una instancia antes de que ServletContext esté listo (los filtros Spring Security deben crearse muy pronto para que esto pueda suceder).


Se encontró con un problema similar. Si desea configurar un evaluador de permisos personalizado en el controlador de expresiones, puede hacer algo como esto

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

Si desea utilizar el evaluador de permisos explícitamente, junto con lo anterior, haga lo que sugiere Dave, es decir, defina el bean evaluador de permisos en su archivo de configuración.