SpringCloud启动zuul时,报如下错误:
查询发现是Springboot的版本问题,我这里springbood版本是2.1.,SpringCloud是F版本。把SpringBoot版本降到2.0.6即可。
2019-03-06 22:26:23.134 WARN 12080 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'proxyRequestHelper' defined in class path resource [org/springframework/cloud/netflix/zuul/ZuulProxyAutoConfiguration$NoActuatorConfiguration.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.cloud.netflix.zuul.ZuulProxyAutoConfiguration$NoActuatorConfiguration; factoryMethodName=proxyRequestHelper; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/cloud/netflix/zuul/ZuulProxyAutoConfiguration$NoActuatorConfiguration.class]] for bean 'proxyRequestHelper': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration$EndpointConfiguration; factoryMethodName=proxyRequestHelper; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/cloud/netflix/zuul/ZuulProxyAutoConfiguration$EndpointConfiguration.class]] bound. 2019-03-06 22:26:23.142 INFO 12080 --- [ main] ConditionEvaluationReportLoggingListener : Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. 2019-03-06 22:26:23.145 ERROR 12080 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPLICATION FAILED TO START *************************** Description: The bean 'proxyRequestHelper', defined in class path resource [org/springframework/cloud/netflix/zuul/ZuulProxyAutoConfiguration$NoActuatorConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/cloud/netflix/zuul/ZuulProxyAutoConfiguration$EndpointConfiguration.class] and overriding is disabled. Action: Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true Disconnected from the target VM, address: '127.0.0.1:61581', transport: 'socket' Process finished with exit code 1Caused 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
我沒有顯式重寫此bean,所以我猜測這只是我們在當前程式碼中所做的副作用。如果我確實允許按照//github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.1-Release-Notes#bean-overriding用spring.main.allow-bean-definition-overriding=true覆蓋bean,那麼我只會得到另一個異常。
java.lang.IllegalStateException: Duplicate key org.springframework.data.spel.ExtensionA[email protected]10dfbbbb at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133) ~[na:1.8.0_162]但是,我什至不想覆蓋任何bean行為,目的是讓自定義許可權評估程式按Spring預期的方式工作。
在上一版本中是這樣的:
在Spring Boot 2.0.6中,我們可以執行以下操作來使自定義PermissionEvaluator類起作用:
擴充套件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) { }; } }然後是一個類,其中使用我們的許可權評估器以及帶有EvaluationContextExtension的@Bean建立表示式處理程式
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(); } }最後,我們將其放在否則為空的類中:
@EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { ... }這是因為如果僅將自定義許可權評估器放在MethodSecurityConfiguration類中,則它永遠不會應用於所有方法。
有問題的伺服器是oauth2資源伺服器,因此我們沒有在WebSecurityConfigurerAdapter中配置其他任何內容。我們還實現了自己的UserDetails,並擴充套件了DefaultUserAuthenticationConverter,如果這與新解決方案有任何關係。
如過時警告中所述,我曾嘗試直接實現EvaluationContextExtension類。通過將擴充套件介面更改為implements EvaluationContextExtension,這只是一個簡單的修改。
我也嘗試過改用看似較新的軟體包org.springframework.data.spel.spi
我試圖刪除我們自己的SecurityEvaluationContextExtension並直接將//docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/data/repository/query/SecurityEvaluationContextExtension.html作為bean返回,但是由於某些原因,Spring Boot 2.1.0中沒有資料包。
我嘗試過完全刪除該bean的定義。
所有這些都會在啟動時導致各種“無效的bean定義”錯誤。
有誰知道在哪裡可以找到遷移指南或其他任何有關該指南現在應該如何工作的資源?
僅供參考,實際的CustomPermissionEvaluator類:
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 } }以及如何使用它的示例:
public interface ProjectRepository extends PagingAndSortingRepository<Project, UUID> { @Override @PreAuthorize("hasPermission(#project, " + Constants.WRITE + ")") <S extends Project> S save(@Param("project") S project); }解決辦法
我獲取了您的程式碼,並從中建立了一個示例應用程式。我在這裡張貼:
//github.com/jzheaux/stackoverflow-53410526
您的@EnableGlobalMethodSecurity批註位於WebSecurityConfigurerAdapter上。您還有一個擴充套件GlobalMethodSecurityConfiguration的類。這有時會在啟動時引起一些排序問題,這可能是您看到的=>建立了兩個MethodSecurityExpressionHandler以及兩個EvaluationContextExtension。
不管這是否是正確的(我猜是這樣),當我將@EnableGlobalMethodSecurity與自定義GlobalMethodSecurityConfiguration匹配時,一切就開始正常了。
而且,儘管如此,您的自定義EvaluationContextExtension似乎與Spring Security的預設值非常相似。如果可以的話,您可以考慮刪除該類以及相應的bean方法,因為當您將spring-boot-starter-security和spring-security-data作為依賴項時,Spring Boot會自動顯示一個類。