Java Spring Security 注销不起作用 - 不清除安全上下文并且经过身份验证的用户仍然存在

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36557294/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 18:25:13  来源:igfitidea点击:

Spring Security logout does not work - does not clear security context and authenticated user still exists

javaspringspring-mvcspring-security

提问by Streetsouls

I know, there are many articles about this topic, but I have a problem and I can't find any solution.

我知道,有很多关于这个主题的文章,但我有一个问题,我找不到任何解决方案。

I have a classic spring security java config:

我有一个经典的 spring 安全 java 配置:

@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);
}
...
}

Also, I have two controller methods, where I login/logout from my web application by AJAX.

此外,我有两个控制器方法,我通过 AJAX 从我的 Web 应用程序登录/注销。

When I would like to logout, I first call this method, which I expect to clear user sessions and clear everything from the security context.

当我想注销时,我首先调用此方法,我希望清除用户会话并清除安全上下文中的所有内容。

@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);
}

After this I reload my client web application and each time, when it is reloaded, I check whether the user is authenticated by calling the following controller method:

在此之后,我重新加载客户端 Web 应用程序,每次重新加载时,我都会通过调用以下控制器方法来检查用户是否已通过身份验证:

@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;
}

And here I aways receive the last authenticated user. It seems that in the previous logout method, Spring logout doesn't work.

在这里,我将收到最后一个经过身份验证的用户。好像在之前的注销方法中,Spring注销是不行的。

Keep in mind that I tried to logout with the following code, without any success:

请记住,我尝试使用以下代码注销,但没有成功:

   @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);
         }
    }

Are you have any idea what I miss in my config and the logout process?

您知道我在配置和注销过程中遗漏了什么吗?

采纳答案by FreezY

From your question,I see you are trying to create your own logout and you also trying to use the default spring logout.I advise you should choose one method only not mixed them both.There are two way I can show you to logout from spring:

从您的问题中,我看到您正在尝试创建自己的注销,并且您还尝试使用默认的 spring 注销。我建议您应该选择一种方法,不要将它们混合使用。我可以通过两种方式向您展示从 spring 注销:

First: Default spring security logout

第一:默认 spring 安全注销

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

From example above, you should only need to call the /logouturl whenever you want to logout the user. No need to create any @Controllerto handle that logout instead spring will help to log the user out. You also can add other thing you want to invalidate here.

从上面的示例中,您应该只需要/logout在想要注销用户时调用url。无需创建任何@Controller来处理该注销,而是 spring 将帮助用户注销。您还可以在此处添加其他要失效的内容。

Second: Proggrammatically logout

第二种:以编程方式注销

@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";
}

If you are using this logout,you doesn't need to include the first method in spring security config. By using this method,you can add extra action to do before and after logout done.Btw, to use this logout ,just call the /logouturl and user will be logout manually.This method will invalidate session,clear spring security context and cookies.

如果您使用此注销,则不需要在 spring 安全配置中包含第一种方法。通过使用此方法,您可以在注销完成之前和之后添加额外的操作。顺便说一句,要使用此注销,只需调用/logouturl,用户将手动注销。此方法将使会话无效,清除 spring 安全上下文和 cookie。

In addition for second method, if you are using RequestMethod.POST, you need to include the csrf key as a post. The alternative way is to create a form with hidden input csrf key. This is some example of auto generated logout link with jquery :

另外对于第二种方法,如果您使用的是RequestMethod.POST,则需要将 csrf 密钥包含在帖子中。另一种方法是创建一个带有隐藏输入 csrf 键的表单。这是使用 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();
});

You just need to create hyperlink <a id="Logout">Logout</a>to use it.

您只需要创建超链接<a id="Logout">Logout</a>即可使用它。

If you are using RequestMethod.GET,just include a csrf key as a parameter in you link like this:

如果您正在使用RequestMethod.GET,只需在链接中包含一个 csrf 键作为参数,如下所示:

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

Thats all,hope its help.

就是这样,希望它的帮助。

回答by Laci

This will help, i think clearAuthentication(true) is enough:

这会有所帮助,我认为 clearAuthentication(true) 就足够了:

@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()

回答by Raf

Just a heads up, there is Clear Site DataHTTP header as shown below

请注意,有如下所示的清除站点数据HTTP 标头

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

I also helped add support for Clear-Site-Dataheader into Spring-Security 5.2project. For more details around the implementation, see the PR.

我还帮助Clear-Site-DataSpring-Security 5.2项目中添加了对标头的支持。有关实现的更多详细信息,请参阅PR

Here is a sample of how it is going to work

这是它如何工作的示例

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

Where SOURCE is a varargof one or more of the following

其中 SOURCE 是vararg以下一项或多项

  • "*"Clear everything
  • One or more of "cache", "cookies", "storage", "executionContexts"
  • "*"清除一切
  • 一项或多项 "cache", "cookies", "storage", "executionContexts"

For more details see the sample test in the LogoutConfigurerClearSiteDataTests.java.

有关更多详细信息,请参阅LogoutConfigurerClearSiteDataTests.java 中的示例测试。

回答by Kaique Dias

I solved my problem similarly by adding the following parameter to the application.properties file

我通过将以下参数添加到 application.properties 文件类似地解决了我的问题

spring.cache.type=NONE

回答by Yogesh Shipkule

enter image description here

在此处输入图片说明

Just change logout URL from "/logout" to "war or snapshot name/logout"

只需将注销 URL 从“/注销”更改为“战争或快照名称/注销”