Сценарий
- Я уже создал учетную запись на своем веб-сайте через логин OAuth2.
- Я вошел в эту учетную запись с
SecurityContext
, заполненным моим зарегистрированным пользователем. - Я хочу связать эту учетную запись с другим поставщиком OAuth2.
- Я хочу отсоединить эту учетную запись от первоначального поставщика OAuth2.
Моя конфигурация безопасности:
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.requiresChannel()
.anyRequest().requiresSecure()
.and()
.headers()
.frameOptions()
.sameOrigin()
.and()
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/register", "/login").permitAll()
.antMatchers("/static/**", "/css/**", "/js/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/?login")
.loginProcessingUrl("/login")
.failureUrl("/?error")
.passwordParameter("password")
.usernameParameter("emailAddress")
.successHandler(authenticationSuccessHandler())
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.and()
.oauth2Login()
.loginPage("/?login")
.failureUrl("/?error")
.successHandler(authenticationSuccessHandler())
.and()
.httpBasic();
}
}
И свойства моего приложения:
spring:
security:
oauth2:
client:
registration:
google:
clientId: <removed>
clientSecret: <removed>
facebook:
clientId: <removed>
clientSecret: <removed>
provider:
facebook:
userInfoUri: https://graph.facebook.com/me?fields=id,email,first_name
Что я пробовал
Первой моей мыслью было добавить еще одну кнопку входа в социальную сеть на экране для аутентифицированных пользователей (примечание: это то же самое, что икнопка входа).Это правильно перенаправляет их к поставщику OAuth2, они входят в систему, а затем перенаправляются обратно в приложение.Проблема заключается в том, что этот процесс проходит обычный процесс фильтрации для «входа в OAuth2», он переключает SecurityContext
, чтобы теперь иметь новые учетные данные OAuth2, теряя того, кто входил в систему раньше.
Это происходит из-за OAuth2LoginAuthenticationFilter
, который расширяет AbstractAuthenticationProcessingFilter
и в методе successfulAuthentication
он очищает контекст безопасности и вставляет новые учетные данные:
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
this.rememberMeServices.loginSuccess(request, response, authResult);
if (this.eventPublisher != null) {
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
this.successHandler.onAuthenticationSuccess(request, response, authResult);
}
Так что я подумал, эй!Почему бы мне не переопределить OAuth2LoginAuthenticationFilter
, а затем реализовать свой собственный successfulAuthentication
, который может определять, если у нас уже есть другой пользователь, вошедший в систему. Если мы этого не сделаем, выполните обычное поведение.Если мы получим этого пользователя, добавьте к нему уникальный идентификатор, предоставленный нам поставщиком OAuth2, и продолжите цепочку фильтров. Важно не изменять SecurityContext
.
Оказалось, что это не очень просто, поскольку жесткие коды OAuth2LoginConfigurer
используют OAuth2LoginAuthenticationFilter
, и вы не можете предоставить другой для использования:
public void init(B http) throws Exception {
OAuth2LoginAuthenticationFilter authenticationFilter = new OAuth2LoginAuthenticationFilter(this.getClientRegistrationRepository(), this.getAuthorizedClientService(), this.loginProcessingUrl);
this.setAuthenticationFilter(authenticationFilter);
...
}
И метод setAuthenticationFilter
не является общедоступным:
protected final void setAuthenticationFilter(F authFilter) {
this.authFilter = authFilter;
}
Я также хотел бы отсоединить пользователя от его логина OAuth2, если он пожелает, и помимо простого удаления егоссылка в базе данных на этот уникальный идентификатор, что еще для этого нужно сделать?
Используемые версии
Spring Boot: 2.0.5.RELEASE
Spring Security: 5.0.8. RELEASE
Заранее спасибо.Я рад предоставить больше информации, если это необходимо.
Примечание Я не верю, что об этом спрашивали раньше.После интенсивного поиска в Google вся документация, по-видимому, относится к входу в приложение с именем входа OAuth2, и ничто не связывает эту учетную запись с другим именем входа OAuth2.