参照官方文档:https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_simple
引入的项目依赖:
org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-security
org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
2.1.4.RELEASE
org.webjars
jquery
2.1.1
org.webjars
bootstrap
3.2.0
org.webjars
webjars-locator-core
org.springframework.boot
spring-boot-configuration-processor
true
问题出现:引入一下代码,启动程序报错:The bean 'oauth2ClientFilterRegistration', defined in class path resource [org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2RestOperationsConfiguration$SessionScopedConfiguration.class], could not be registered. A bean with that name has already been defined in com.hegret.SocialApplication and overriding is disabled.
@Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.4.RELEASE)
2019-06-05 14:42:20.603 INFO 8560 --- [ main] com.hegret.SocialApplication : Starting SocialApplication on zsx with PID 8560 (F:\workspace-test\spring-oauth2-server\target\classes started by zhang in F:\workspace-test\spring-oauth2-server)
2019-06-05 14:42:20.605 INFO 8560 --- [ main] com.hegret.SocialApplication : No active profile set, falling back to default profiles: default
2019-06-05 14:42:20.877 WARN 8560 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'oauth2ClientFilterRegistration' defined in class path resource [org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2RestOperationsConfiguration$SessionScopedConfiguration.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.boot.autoconfigure.security.oauth2.client.OAuth2RestOperationsConfiguration$SessionScopedConfiguration; factoryMethodName=oauth2ClientFilterRegistration; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2RestOperationsConfiguration$SessionScopedConfiguration.class]] for bean 'oauth2ClientFilterRegistration': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=socialApplication; factoryMethodName=oauth2ClientFilterRegistration; initMethodName=null; destroyMethodName=(inferred); defined in com.hegret.SocialApplication] bound.
2019-06-05 14:42:20.883 INFO 8560 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-06-05 14:42:20.884 ERROR 8560 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'oauth2ClientFilterRegistration', defined in class path resource [org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2RestOperationsConfiguration$SessionScopedConfiguration.class], could not be registered. A bean with that name has already been defined in com.hegret.SocialApplication and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
问题分析:跟踪源码,在OAuth2RestOperationsConfiguration类中已经定义过该bean对象了
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.client;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter;
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
import org.springframework.security.oauth2.config.annotation.web.configuration.OAuth2ClientConfiguration;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.util.StringUtils;
/**
* Configuration for OAuth2 Single Sign On REST operations.
*
* @author Dave Syer
* @author Madhura Bhave
* @since 1.3.0
*/
@Configuration
@ConditionalOnClass(EnableOAuth2Client.class)
public class OAuth2RestOperationsConfiguration {
@Configuration
@Conditional(ClientCredentialsCondition.class)
protected static class SingletonScopedConfiguration {
@Bean
@ConfigurationProperties(prefix = "security.oauth2.client")
@Primary
public ClientCredentialsResourceDetails oauth2RemoteResource() {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
return details;
}
@Bean
public DefaultOAuth2ClientContext oauth2ClientContext() {
return new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest());
}
}
@Configuration
@ConditionalOnBean(OAuth2ClientConfiguration.class)
@Conditional({ OAuth2ClientIdCondition.class, NoClientCredentialsCondition.class })
@Import(OAuth2ProtectedResourceDetailsConfiguration.class)
protected static class SessionScopedConfiguration {
@Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(
OAuth2ClientContextFilter filter, SecurityProperties security) {
FilterRegistrationBean registration = new FilterRegistrationBean<>();
registration.setFilter(filter);
registration.setOrder(security.getFilter().getOrder() - 10);
return registration;
}
}
// When the authentication is per cookie but the stored token is an oauth2 one, we can
// pass that on to a client that wants to call downstream. We don't even need an
// OAuth2ClientContextFilter until we need to refresh the access token. To handle
// refresh tokens you need to @EnableOAuth2Client
@Configuration
@ConditionalOnMissingBean(OAuth2ClientConfiguration.class)
@Conditional({ OAuth2ClientIdCondition.class, NoClientCredentialsCondition.class })
@Import(OAuth2ProtectedResourceDetailsConfiguration.class)
protected static class RequestScopedConfiguration {
@Bean
@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public DefaultOAuth2ClientContext oauth2ClientContext() {
DefaultOAuth2ClientContext context = new DefaultOAuth2ClientContext(
new DefaultAccessTokenRequest());
Authentication principal = SecurityContextHolder.getContext()
.getAuthentication();
if (principal instanceof OAuth2Authentication) {
OAuth2Authentication authentication = (OAuth2Authentication) principal;
Object details = authentication.getDetails();
if (details instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails oauthsDetails = (OAuth2AuthenticationDetails) details;
String token = oauthsDetails.getTokenValue();
context.setAccessToken(new DefaultOAuth2AccessToken(token));
}
}
return context;
}
}
/**
* Condition to check if a {@code security.oauth2.client.client-id} is specified.
*/
static class OAuth2ClientIdCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String clientId = context.getEnvironment()
.getProperty("security.oauth2.client.client-id");
ConditionMessage.Builder message = ConditionMessage
.forCondition("OAuth Client ID");
if (StringUtils.hasLength(clientId)) {
return ConditionOutcome.match(message
.foundExactly("security.oauth2.client.client-id property"));
}
return ConditionOutcome.noMatch(message
.didNotFind("security.oauth2.client.client-id property").atAll());
}
}
/**
* Condition to check for no client credentials.
*/
static class NoClientCredentialsCondition extends NoneNestedConditions {
NoClientCredentialsCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@Conditional(ClientCredentialsCondition.class)
static class ClientCredentialsActivated {
}
}
/**
* Condition to check for client credentials.
*/
static class ClientCredentialsCondition extends AnyNestedCondition {
ClientCredentialsCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnProperty(prefix = "security.oauth2.client", name = "grant-type", havingValue = "client_credentials", matchIfMissing = false)
static class ClientCredentialsConfigured {
}
@ConditionalOnNotWebApplication
static class NoWebApplication {
}
}
}
解决方法:
在application.yml文件中添加配置,对bean进行重新定义覆盖
spring:
main:
allow-bean-definition-overriding: true
重新启动,运行正常
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.4.RELEASE)
2019-06-05 15:34:51.519 INFO 724 --- [ main] com.hegret.SocialApplication : Starting SocialApplication on zsx with PID 724 (F:\workspace-test\spring-oauth2-server\target\classes started by zhang in F:\workspace-test\spring-oauth2-server)
2019-06-05 15:34:51.521 INFO 724 --- [ main] com.hegret.SocialApplication : No active profile set, falling back to default profiles: default
2019-06-05 15:34:52.069 INFO 724 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2019-06-05 15:34:52.081 INFO 724 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2019-06-05 15:34:52.081 INFO 724 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.17]
2019-06-05 15:34:52.147 INFO 724 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-06-05 15:34:52.147 INFO 724 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 606 ms
2019-06-05 15:34:52.312 INFO 724 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-06-05 15:34:52.358 INFO 724 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html]
2019-06-05 15:34:52.398 INFO 724 --- [ main] .s.s.UserDetailsServiceAutoConfiguration :
Using generated security password: d8670419-1986-4c55-a811-e147c18f0174
2019-06-05 15:34:52.444 INFO 724 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/**'], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@1cb19dba, org.springframework.security.web.context.SecurityContextPersistenceFilter@6e106680, org.springframework.security.web.header.HeaderWriterFilter@16c8b7bd, org.springframework.security.web.csrf.CsrfFilter@4dafba3e, org.springframework.security.web.authentication.logout.LogoutFilter@2f5c1332, org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter@7c3ebc6b, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@55ecbafe, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@205b132e, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1931d99, org.springframework.security.web.session.SessionManagementFilter@65bcf7c2, org.springframework.security.web.access.ExceptionTranslationFilter@476a736d, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@5bca7664]
2019-06-05 15:34:52.479 INFO 724 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-06-05 15:34:52.481 INFO 724 --- [ main] com.hegret.SocialApplication : Started SocialApplication in 1.146 seconds (JVM running for 1.472)