web-dev-qa-db-fra.com

La déconnexion de Spring Security ne fonctionne pas - n'efface pas le contexte de sécurité et l'utilisateur authentifié existe toujours

Je sais, il y a beaucoup d'articles sur ce sujet, mais j'ai un problème et je ne trouve aucune solution.

J'ai une sécurité de printemps classique Java config:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private AuctionAuthenticationProvider auctionAuthenticationProvider;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(auctionAuthenticationProvider);
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic();

    ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequest = http.authorizeRequests();

    configureAdminPanelAccess(authorizeRequest);
    configureFrontApplicationAccess(authorizeRequest);
    configureCommonAccess(authorizeRequest);

    http.csrf()
        .csrfTokenRepository(csrfTokenRepository()).and()
        .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);

    http.logout()
        .clearAuthentication(true)
        .invalidateHttpSession(true);
}
...
}

En outre, j'ai deux méthodes de contrôleur, où je me connecte/me déconnecte de mon application Web par AJAX.

Lorsque je souhaite me déconnecter, j'appelle d'abord cette méthode, qui devrait, selon moi, effacer les sessions utilisateur et tout effacer du contexte de sécurité.

@Override
@RequestMapping(value = "/logout", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<Boolean> logout(final HttpServletRequest request, final HttpServletResponse response) {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if (auth != null){
        new SecurityContextLogoutHandler().logout(request, response, auth);
    }

    return new ResponseEntity<>(Boolean.TRUE, HttpStatus.OK);
}

Après cela, je recharge mon application Web cliente et chaque fois qu'elle est rechargée, je vérifie si l'utilisateur est authentifié en appelant la méthode de contrôleur suivante:

@Override
@RequestMapping(value = "/user", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<UserDetails> user() {
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    if (principal instanceof UserDetails) {
        return new ResponseEntity<>((UserDetails) principal, HttpStatus.OK);
    }

    return null;
}

Et ici, je reçois toujours le dernier utilisateur authentifié. Il semble que dans la méthode de déconnexion précédente, la déconnexion de printemps ne fonctionne pas.

Gardez à l'esprit que j'ai essayé de me déconnecter avec le code suivant, sans succès:

   @Override
   @RequestMapping(value = "/logout", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Boolean> logout(final HttpServletRequest request) {
     try {
         request.logout();

         return new ResponseEntity<>(Boolean.TRUE, HttpStatus.OK);
     } catch (ServletException ex) {
         if (LOG.isDebugEnabled()) {
             LOG.debug("There is a problem with the logout of the user", ex);
         }
    }

Avez-vous une idée de ce qui me manque dans ma configuration et le processus de déconnexion?

10
Streetsouls

D'après votre question, je vois que vous essayez de créer votre propre déconnexion et que vous essayez également d'utiliser la déconnexion par défaut du printemps.Je vous conseille de choisir une méthode, mais pas de les mélanger toutes les deux. :

Premier: déconnexion de sécurité par défaut du printemps

.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/logout.done").deleteCookies("JSESSIONID")
.invalidateHttpSession(true) 

Dans l'exemple ci-dessus, vous devez uniquement appeler le /logout url chaque fois que vous souhaitez déconnecter l'utilisateur. Pas besoin de créer de @Controller pour gérer cette déconnexion à la place le printemps aidera à déconnecter l'utilisateur. Vous pouvez également ajouter autre chose que vous souhaitez invalider ici.

Deuxième: Déconnexion progressive

@RequestMapping(value = {"/logout"}, method = RequestMethod.POST)
public String logoutDo(HttpServletRequest request,HttpServletResponse response){
HttpSession session= request.getSession(false);
    SecurityContextHolder.clearContext();
         session= request.getSession(false);
        if(session != null) {
            session.invalidate();
        }
        for(Cookie cookie : request.getCookies()) {
            cookie.setMaxAge(0);
        }

    return "logout";
}

Si vous utilisez cette déconnexion, vous n'avez pas besoin d'inclure la première méthode dans la configuration de sécurité du printemps. En utilisant cette méthode, vous pouvez ajouter une action supplémentaire à effectuer avant et après la déconnexion. Pour utiliser cette déconnexion, il suffit d'appeler le /logout l'url et l'utilisateur se déconnecteront manuellement. Cette méthode invalidera la session, effacera le contexte de sécurité du printemps et les cookies.

De plus, pour la deuxième méthode, si vous utilisez RequestMethod.POST, vous devez inclure la clé csrf en tant que publication. L'autre manière consiste à créer un formulaire avec une clé d'entrée csrf cachée. Voici un exemple de lien de déconnexion généré automatiquement avec jquery:

$("#Logout").click(function(){
    $form=$("<form>").attr({"action":"${pageContext.request.contextPath}"+"/logout","method":"post"})
    .append($("<input>").attr({"type":"hidden","name":"${_csrf.parameterName}","value":"${_csrf.token}"}))
    $("#Logout").append($form);
    $form.submit();
});

Il vous suffit de créer un hyperlien <a id="Logout">Logout</a> pour l'utiliser.

Si vous utilisez RequestMethod.GET, il suffit d'inclure une clé csrf comme paramètre dans votre lien comme ceci:

<a href="${pageContext.request.contextPath}/logout?${_csrf.parameterName}=${_csrf.token}">Logout</a>

C'est tout, espérons son aide.

31
FreezY

Cela aidera, je pense que clearAuthentication (true) suffit:

@Configuration 
@EnableWebSecurity 
public class SecurityConfig extends WebSecurityConfigurerAdapter {

....

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        http
        .httpBasic()
        .and()
        .logout().clearAuthentication(true)
        .logoutSuccessUrl("/")
        .deleteCookies("JSESSIONID")
        .invalidateHttpSession(true)
        .and()
1
Laci

J'ai résolu mon problème de la même manière en ajoutant le paramètre suivant au fichier application.properties

spring.cache.type=NONE
0
Kaique Dias

Attention, il y a Clear Site Data en-tête HTTP comme indiqué ci-dessous

Clear-Site-Data: "cache", "cookies", "storage", "executionContexts"

J'ai également aidé à ajouter la prise en charge de Clear-Site-Data en-tête dans Spring-Security 5.2 projet. Pour plus de détails sur l'implémentation, voir PR .

Voici un exemple de la façon dont cela va fonctionner

@EnableWebSecurity
static class HttpLogoutConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .logout()
                    .addLogoutHandler(new HeaderWriterLogoutHandler(
                           new ClearSiteDataHeaderWriter(SOURCE)));
    }
}

Où SOURCE est un vararg d'un ou plusieurs des éléments suivants

  • "*" Tout effacer
  • Un ou plusieurs des "cache", "cookies", "storage", "executionContexts"

Pour plus de détails, voir l'exemple de test dans LogoutConfigurerClearSiteDataTests.Java .

0
Raf