Dans Spring Boot 2.1.0, EvaluationContextExtensionSupport
est obsolète et https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/query/spi/ EvaluationContextExtensionSupport.html dit à implémente directement EvaluationContextExtension
Même s'il est seulement obsolète, il échoue instantanément sur cette mise à jour avec cette pile:
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
Je ne remplace pas explicitement ce haricot, alors je suppose que ce n'est qu'un effet secondaire de ce que nous faisons dans notre code actuel. Si j'autorise le bean écrasant avec spring.main.allow-bean-definition-overriding=true
conformément à https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.1-Release-Notes#bean-overriding alors j'obtiens simplement un autre exception.
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]
Cependant, je ne veux même pas remplacer le comportement d'un bean, l'objectif est de faire fonctionner à nouveau un évaluateur d'autorisations personnalisées comme prévu par Spring.
Voici comment cela fonctionnait dans la dernière version:
Dans Spring Boot 2.0.6, nous avions les éléments suivants pour que notre classe personnalisée PermissionEvaluator fonctionne:
Une classe qui a étendu 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) {
};
}
}
Et puis une classe où le gestionnaire d’expression est créé avec notre évaluateur d’autorisation et avec un @Bean avec un 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();
}
}
Et enfin, nous avons ceci dans une classe par ailleurs généralement vide:
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
...
}
En effet, l'évaluateur d'autorisation personnalisé ne s'est jamais appliqué à toutes les méthodes si nous l'inscrivions dans la classe MethodSecurityConfiguration
... Le serveur en question est un serveur de ressources oauth2, de sorte que nous ne configurons rien d'autre dans la WebSecurityConfigurerAdapter
. Nous implémentons également notre propre UserDetails
et nous étendons le DefaultUserAuthenticationConverter
, si cela est pertinent pour la nouvelle solution.
J'ai essayé d'implémenter directement EvaluationContextExtension
class, comme indiqué dans l'avertissement relatif à la dépréciation. C'est juste une simple modification en changeant l'interface étendue en implements EvaluationContextExtension
. J'ai aussi essayé de changer pour le paquet apparemment plus récent org.springframework.data.spel.spi
J'ai essayé de supprimer notre propre SecurityEvaluationContextExtension
et de renvoyer https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/data/repository/query/ SecurityEvaluationContextExtension.html directement sous forme de bean, mais pour une raison quelconque, ce paquet de données n'est pas disponible dans Spring Boot 2.1.0
J'ai simplement essayé de supprimer la définition de ce haricot.
Toutes ces choses entraînent différentes erreurs de 'définition du bean invalide' au démarrage.
Quelqu'un sait-il où trouver un guide de migration ou toute autre ressource expliquant comment cela est censé fonctionner maintenant?
Juste pour référence, la classe CustomPermissionEvaluator
actuelle:
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
}
}
Et un exemple sur la façon dont il est utilisé:
public interface ProjectRepository extends PagingAndSortingRepository<Project, UUID> {
@Override
@PreAuthorize("hasPermission(#project, " + Constants.WRITE + ")")
<S extends Project> S save(@Param("project") S project);
}
J'ai pris votre code et créé un exemple d'application à partir de celui-ci. Je l'ai posté ici:
https://github.com/jzheaux/stackoverflow-53410526
Votre annotation @EnableGlobalMethodSecurity
est sur WebSecurityConfigurerAdapter
. Vous avez également une classe qui étend GlobalMethodSecurityConfiguration
. Cela peut parfois poser des problèmes de classement au démarrage, ce qui peut correspondre à ce que vous voyez => deux MethodSecurityExpressionHandler
s sont créés ainsi que deux EvaluationContextExtension
s.
Que ce soit précisément ou non le cas (j'imagine que c'est le cas), lorsque j'ai fait correspondre votre @EnableGlobalMethodSecurity
avec votre GlobalMethodSecurityConfiguration
personnalisé, tout a bien commencé.
De plus, il semble que votre EvaluationContextExtension
personnalisée soit très similaire à la sécurité par défaut de Spring. Vous pouvez envisager de supprimer cette classe ainsi que la méthode de bean correspondante, si vous le pouvez, car Spring Boot en expose une automatiquement lorsque vous avez les dépendances spring-boot-starter-security
et spring-security-data
.
Vous écrasez methodSecurityInterceptor
bean. Cela fonctionnait auparavant car la suppression des haricots était autorisée.
La substitution de bean a été désactivée par défaut pour éviter la substitution accidentelle d'un bean. Si vous comptez sur le remplacement, vous devez définir spring.main.allow-bean-definition-override sur true.