Spring 用作登录提供商的社交 Google 在成功登录 Oauth2 后失败并显示 403
Spring Social Google used as Sign in Provider fails with 403 after successful Oauth2 login
我需要同时使用 Facebook 和 Google 作为提供商中的 OpenId Sing。我已使用 SocialAuthenticationFilter 将它们与 Spring 安全性集成,如 documentation 中所述,并查看示例应用程序。
我已成功配置 Facebook。
问题是当我尝试使用 Google:
进行身份验证时
在OAuth2AuthenticationService.getAuthToken():
...
AccessGrant accessGrant = getConnectionFactory().getOAuthOperations().exchangeForAccess(code, returnToUrl, null);
此时我可以看到accessGrant包含了一个accessToken,所以到目前为止它似乎是正确的。它在以下调用中失败:
// TODO avoid API call if possible (auth using token would be fine)
Connection<S> connection = getConnectionFactory().createConnection(accessGrant);
createConnection()
结束调用 GoogleConnectionFactory.extractProviderUserId(AccessGrant accessGrant)
:
Google api = ((GoogleServiceProvider)getServiceProvider()).getApi(accessGrant.getAccessToken());
UserProfile userProfile = getApiAdapter().fetchUserProfile(api);
...
和 getApiAdapter().fetchUserProfile(Google)
-> google.plusOperations().getGoogleProfile();
抛出 403 异常:
org.springframework.web.client.HttpClientErrorException: 403 Forbidden
为什么无法获取 Google 配置文件?显然我设置的范围和提示给用户的是正确的...
完整项目可在此处获得:https://github.com/codependent/spring-boot-social-signin
配置摘录:
安全配置:
@EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/secure*").authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
//.loginProcessingUrl("/secure-home")
.failureUrl("/login?param.error=bad_credentials")
.and()
.logout()
.logoutUrl("/logout")
.deleteCookies("JSESSIONID")
.and()
/*.rememberMe()
.and()*/
.apply(new SpringSocialConfigurer());
}
@Bean
public SocialUserDetailsService socialUserDetailsService(){
return new SocialUserDetailsService(){
@Override
public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException{
return new SimpleSocialUserDetails(userId);
}
}
}
}
社交配置:
@Configuration
@EnableSocial
class SocialConfig extends SocialConfigurerAdapter{
@Override
void addConnectionFactories(ConnectionFactoryConfigurer cfConfig, Environment env) {
FacebookConnectionFactory fcf = new FacebookConnectionFactory(env.getProperty("facebook.clientId"), env.getProperty("facebook.clientSecret"))
fcf.setScope("public_profile,email")
cfConfig.addConnectionFactory(fcf)
GoogleConnectionFactory gcf = new GoogleConnectionFactory(env.getProperty("google.clientId"), env.getProperty("google.clientSecret"))
gcf.setScope("openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/tasks https://www-opensocial.googleusercontent.com/api/people https://www.googleapis.com/auth/plus.login");
cfConfig.addConnectionFactory(gcf);
}
@Bean
@Scope(value="request", proxyMode=ScopedProxyMode.INTERFACES)
Facebook facebook(ConnectionRepository repository) {
Connection<Facebook> connection = repository.findPrimaryConnection(Facebook.class);
return connection != null ? connection.getApi() : null;
}
@Bean
@Scope(value="request", proxyMode=ScopedProxyMode.INTERFACES)
Google google(ConnectionRepository repository) {
Connection<Google> connection = repository.findPrimaryConnection(Google.class);
return connection != null ? connection.getApi() : null;
}
@Override
UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
//return new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText());
InMemoryUsersConnectionRepository rep = new InMemoryUsersConnectionRepository(connectionFactoryLocator)
rep.setConnectionSignUp(new ConnectionSignUp(){
public String execute(Connection<?> connection){
Facebook facebook = (Facebook)connection.getApi();
String [] fields = [ "id", "email", "first_name", "last_name", "about" , "gender" ];
User userProfile = facebook.fetchObject(connection.getKey().getProviderUserId(), User.class, fields);
return userProfile.getEmail();
}
})
return rep;
}
@Override
UserIdSource getUserIdSource() {
return new AuthenticationNameUserIdSource()
}
}
已修复,我必须在 Google 开发者控制台上启用 Google+ API。
我需要同时使用 Facebook 和 Google 作为提供商中的 OpenId Sing。我已使用 SocialAuthenticationFilter 将它们与 Spring 安全性集成,如 documentation 中所述,并查看示例应用程序。
我已成功配置 Facebook。
问题是当我尝试使用 Google:
进行身份验证时在OAuth2AuthenticationService.getAuthToken():
...
AccessGrant accessGrant = getConnectionFactory().getOAuthOperations().exchangeForAccess(code, returnToUrl, null);
此时我可以看到accessGrant包含了一个accessToken,所以到目前为止它似乎是正确的。它在以下调用中失败:
// TODO avoid API call if possible (auth using token would be fine)
Connection<S> connection = getConnectionFactory().createConnection(accessGrant);
createConnection()
结束调用 GoogleConnectionFactory.extractProviderUserId(AccessGrant accessGrant)
:
Google api = ((GoogleServiceProvider)getServiceProvider()).getApi(accessGrant.getAccessToken());
UserProfile userProfile = getApiAdapter().fetchUserProfile(api);
...
和 getApiAdapter().fetchUserProfile(Google)
-> google.plusOperations().getGoogleProfile();
抛出 403 异常:
org.springframework.web.client.HttpClientErrorException: 403 Forbidden
为什么无法获取 Google 配置文件?显然我设置的范围和提示给用户的是正确的...
完整项目可在此处获得:https://github.com/codependent/spring-boot-social-signin
配置摘录:
安全配置:
@EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/secure*").authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
//.loginProcessingUrl("/secure-home")
.failureUrl("/login?param.error=bad_credentials")
.and()
.logout()
.logoutUrl("/logout")
.deleteCookies("JSESSIONID")
.and()
/*.rememberMe()
.and()*/
.apply(new SpringSocialConfigurer());
}
@Bean
public SocialUserDetailsService socialUserDetailsService(){
return new SocialUserDetailsService(){
@Override
public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException{
return new SimpleSocialUserDetails(userId);
}
}
}
}
社交配置:
@Configuration
@EnableSocial
class SocialConfig extends SocialConfigurerAdapter{
@Override
void addConnectionFactories(ConnectionFactoryConfigurer cfConfig, Environment env) {
FacebookConnectionFactory fcf = new FacebookConnectionFactory(env.getProperty("facebook.clientId"), env.getProperty("facebook.clientSecret"))
fcf.setScope("public_profile,email")
cfConfig.addConnectionFactory(fcf)
GoogleConnectionFactory gcf = new GoogleConnectionFactory(env.getProperty("google.clientId"), env.getProperty("google.clientSecret"))
gcf.setScope("openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/tasks https://www-opensocial.googleusercontent.com/api/people https://www.googleapis.com/auth/plus.login");
cfConfig.addConnectionFactory(gcf);
}
@Bean
@Scope(value="request", proxyMode=ScopedProxyMode.INTERFACES)
Facebook facebook(ConnectionRepository repository) {
Connection<Facebook> connection = repository.findPrimaryConnection(Facebook.class);
return connection != null ? connection.getApi() : null;
}
@Bean
@Scope(value="request", proxyMode=ScopedProxyMode.INTERFACES)
Google google(ConnectionRepository repository) {
Connection<Google> connection = repository.findPrimaryConnection(Google.class);
return connection != null ? connection.getApi() : null;
}
@Override
UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
//return new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText());
InMemoryUsersConnectionRepository rep = new InMemoryUsersConnectionRepository(connectionFactoryLocator)
rep.setConnectionSignUp(new ConnectionSignUp(){
public String execute(Connection<?> connection){
Facebook facebook = (Facebook)connection.getApi();
String [] fields = [ "id", "email", "first_name", "last_name", "about" , "gender" ];
User userProfile = facebook.fetchObject(connection.getKey().getProviderUserId(), User.class, fields);
return userProfile.getEmail();
}
})
return rep;
}
@Override
UserIdSource getUserIdSource() {
return new AuthenticationNameUserIdSource()
}
}
已修复,我必须在 Google 开发者控制台上启用 Google+ API。