En la primavera de arranque 2.1.0 EvaluationContextExtensionSupport
es obsoleto y https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/query/spi/EvaluationContextExtensionSupport.html dice a Implementar EvaluationContextExtension directamente
A pesar de que sólo está en desuso, al instante de comenzar su defecto en esta actualización con este StackTrace:
Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'methodSecurityInterceptor' defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; factoryMethodName=methodSecurityInterceptor; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]] for bean 'methodSecurityInterceptor': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=methodSecurityConfiguration; factoryMethodName=methodSecurityInterceptor; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [ournamespace/configuration/MethodSecurityConfiguration.class]] bound.
at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:894)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(ConfigurationClassBeanDefinitionReader.java:274)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:141)
...and so on
Yo no anulan explícitamente esta frijol, así que supongo que esto es sólo un efecto secundario de lo que estamos haciendo en nuestro código actual. Si hago permitir frijol con anulando spring.main.allow-bean-definition-overriding=true
según https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.1-Release-Notes#bean-overriding entonces simplemente obtener otra excepción.
java.lang.IllegalStateException: Duplicate key org.springframework.data.spel.ExtensionAwareEvaluationContextProvider$EvaluationContextExtensionAdapter@10dfbbbb at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133) ~[na:1.8.0_162]
Sin embargo, no quiero ni para anular cualquier comportamiento frijol, el objetivo es conseguir un evaluador permiso personalizado a trabajar de nuevo el camino de Primavera destina el mismo trabajo.
Así es como lo hizo el trabajo en la última versión:
En la primavera de arranque 2.0.6 tuvimos el siguiente para conseguir nuestra clase PermissionEvaluator personalizado para el trabajo:
Una clase que se extendía EvaluationContextExtensionSupport
import org.springframework.data.repository.query.spi.EvaluationContextExtensionSupport;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
public class SecurityEvaluationContextExtension extends EvaluationContextExtensionSupport {
@Override
public String getExtensionId() {
return "security";
}
@Override
public SecurityExpressionRoot getRootObject() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return new SecurityExpressionRoot(authentication) {
};
}
}
Y luego una clase donde se crea el manejador de expresión con nuestro evaluador permiso, y con un @Bean con una EvaluationContextExtension
import ournamespace.security.CustomPermissionEvaluator;
import ournamespace.security.SecurityEvaluationContextExtension;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.repository.query.spi.EvaluationContextExtension;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
@Configuration
@RequiredArgsConstructor
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
private final CustomPermissionEvaluator permissionEvaluator;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler =
new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(permissionEvaluator);
return expressionHandler;
}
@Bean
EvaluationContextExtension securityExtension() {
return new SecurityEvaluationContextExtension();
}
}
Y por último tenemos esto en una clase de otra manera casi vacío:
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
...
}
Esto se debe a que el evaluador de permisos personalizado no se aplicaba a todos los métodos si sólo hay que poner esto en la MethodSecurityConfiguration
clase. El servidor en cuestión es un servidor de recursos de oauth2 por lo que hacemos todo lo que no configure otro lugar del WebSecurityConfigurerAdapter
. También implementamos nuestra propia UserDetails
y extendemos el DefaultUserAuthenticationConverter
, si esto es de alguna manera relevante para la nueva solución.
He tratado de implementar EvaluationContextExtension
la clase directamente, como se indica en el aviso de desaprobación. Es sólo una simple modificación cambiando la interfaz se extiende a implements EvaluationContextExtension
. También he intentado cambiar al paquete aparentemente más recienteorg.springframework.data.spel.spi
He intentado borrar nuestra propia SecurityEvaluationContextExtension
y volviendo https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/data/repository/query/SecurityEvaluationContextExtension.html como un grano directamente pero por alguna razón ese paquete de datos no está disponible en la primavera de arranque 2.1.0
He intentado sólo la eliminación de la definición de ese frijol por completo.
Todas estas cosas dan lugar a varios 'definición de frijol no válidos' errores en el arranque.
¿Alguien sabe dónde encontrar una guía de migración o cualquier otro recurso sobre cómo se supone que funciona ahora?
Sólo por el bien de referencia, el actual CustomPermissionEvaluator
clase:
import ournamespace.configuration.Constants;
import ournamespace.exception.InternalException;
import ournamespace.model.Account;
import ournamespace.model.Member;
import ournamespace.model.Project;
import ournamespace.repository.MemberRepository;
import ournamespace.service.ServiceUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import static ournamespace.model.MemberStatus.JOINED;
import static ournamespace.model.ProjectRole.*;
@RequiredArgsConstructor
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
private final MemberRepository memberRepository;
@Override
public boolean hasPermission(Authentication auth, Object targetDomainObject, Object permission) {
if (targetDomainObject == null)
return false;
if (!(permission instanceof String))
return false;
if (auth == null)
return false;
Account account = ServiceUtil.getAccount(auth);
if (targetDomainObject instanceof Project)
return hasPermissionOnProject(account, (Project) targetDomainObject, (String) permission);
//and so on
}
}
Y un ejemplo de cómo se usa:
public interface ProjectRepository extends PagingAndSortingRepository<Project, UUID> {
@Override
@PreAuthorize("hasPermission(#project, " + Constants.WRITE + ")")
<S extends Project> S save(@Param("project") S project);
}
Tomé su código y creado una aplicación de ejemplo de ella. He publicado aquí:
https://github.com/jzheaux/stackoverflow-53410526
Su @EnableGlobalMethodSecurity
anotación está en WebSecurityConfigurerAdapter
. También tiene una clase que amplíe GlobalMethodSecurityConfiguration
. Esto puede causar algunos problemas en el arranque de pedidos, a veces, que puede ser lo que está viendo => dos MethodSecurityExpressionHandler
s se crean, así como dos EvaluationContextExtension
s.
Si este es precisamente el caso o no (supongo que lo es), cuando coincide con su @EnableGlobalMethodSecurity
con su costumbre GlobalMethodSecurityConfiguration
, las cosas empezaron a subir bien.
También, sin embargo, parece que su costumbre EvaluationContextExtension
es muy similar a la forma predeterminada Spring Security. Se podría considerar la eliminación de esa clase, así como el método de frijol correspondiente, si es posible, desde la primavera de arranque expone uno de forma automática cuando se tiene spring-boot-starter-security
y spring-security-data
como dependencias.