Как выйти из системы при использовании Spring WebFlux и OAuth 2.0 Login? - PullRequest
0 голосов
/ 08 января 2020

У меня есть следующее LogoutResource, которое хорошо работает с Spring Boot 2.2 при использовании Spring MVC:

@RestController
public class LogoutResource {
    private ClientRegistration registration;

    public LogoutResource(ClientRegistrationRepository registrations) {
        this.registration = registrations.findByRegistrationId("oidc");
    }

    /**
     * {@code POST  /api/logout} : logout the current user.
     *
     * @param request the {@link HttpServletRequest}.
     * @param idToken the ID token.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and a body with a global logout URL and ID token.
     */
    @PostMapping("/api/logout")
    public ResponseEntity<?> logout(HttpServletRequest request,
                                    @AuthenticationPrincipal(expression = "idToken") OidcIdToken idToken) {
        String logoutUrl = this.registration.getProviderDetails()
            .getConfigurationMetadata().get("end_session_endpoint").toString();

        Map<String, String> logoutDetails = new HashMap<>();
        logoutDetails.put("logoutUrl", logoutUrl);
        logoutDetails.put("idToken", idToken.getTokenValue());
        request.getSession().invalidate();
        return ResponseEntity.ok().body(logoutDetails);
    }
}

Клиент Angular получает этот ответ и перенаправляет его в Keycloak для выхода из системы. Это все прекрасно работает.

logout(): void {
  this.authServerProvider.logout().subscribe((logout: Logout) => {
    let logoutUrl = logout.logoutUrl;
    const redirectUri = `${location.origin}${this.location.prepareExternalUrl('/')}`;

    // if Keycloak, uri has protocol/openid-connect/token
    if (logoutUrl.includes('/protocol')) {
      logoutUrl = logoutUrl + '?redirect_uri=' + redirectUri;
    } else {
      // Okta
      logoutUrl = logoutUrl + '?id_token_hint=' + logout.idToken + '&post_logout_redirect_uri=' + redirectUri;
    }
    window.location.href = logoutUrl;
  });
}

Теперь я пытаюсь изменить его, чтобы он работал с Spring WebFlux. Кажется, что следующее работает и возвращает ожидаемый ответ (я подтвердил это, напечатав значения в моем Angular logout() методе).

@RestController
public class LogoutResource {
    private Mono<ClientRegistration> registration;

    public LogoutResource(ReactiveClientRegistrationRepository registrations) {
        this.registration = registrations.findByRegistrationId("oidc");
    }

    /**
     * {@code POST  /api/logout} : logout the current user.
     *
     * @param idToken the ID token.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and a body with a global logout URL and ID token.
     */
    @PostMapping("/api/logout")
    public Mono<Map<String, String>> logout(@AuthenticationPrincipal(expression = "idToken") OidcIdToken idToken) {
        return this.registration.map(oidc ->
            oidc.getProviderDetails().getConfigurationMetadata().get("end_session_endpoint").toString())
            .map(logoutUrl -> {
                Map<String, String> logoutDetails = new HashMap<>();
                logoutDetails.put("logoutUrl", logoutUrl);
                logoutDetails.put("idToken", idToken.getTokenValue());
                SecurityContextHolder.clearContext();
                return logoutDetails;
            });
    }
}

Вы могли заметить, что я попытался использовать SecurityContextHolder.clearContext() вместо request.getSession().invalidate(). Кажется, это не работает, потому что пользователь все еще вошел в систему. Есть идеи, что мне нужно сделать, чтобы завершить сеанс безопасности пользователя с помощью WebFlux?

1 Ответ

1 голос
/ 12 января 2020

Мне удалось это исправить, введя WebSession и вызвав invalidate().

@PostMapping("/api/logout")
public Mono<Map<String, String>> logout(@AuthenticationPrincipal(expression = "idToken") OidcIdToken idToken, WebSession session) {
    return session.invalidate().then(
        this.registration.map(oidc -> oidc.getProviderDetails().getConfigurationMetadata().get("end_session_endpoint").toString())
            .map(logoutUrl -> {
                Map<String, String> logoutDetails = new HashMap<>();
                logoutDetails.put("logoutUrl", logoutUrl);
                logoutDetails.put("idToken", idToken.getTokenValue());
                return logoutDetails;
            })
    );
}
...