spring SpringSecurity:无法删除 JSESSIONID

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15946132/
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-09-08 05:51:45  来源:igfitidea点击:

SpringSecurity: Fail to delete JSESSIONID

springspring-mvcspring-security

提问by user1764823

I need to delete the cookie JSESSIONID when the user logs out. To do that I have added the following configuration to my security config:

当用户注销时,我需要删除 cookie JSESSIONID。为此,我在安全配置中添加了以下配置:

<http>
    <form-login login-page="/login*" authentication-failure-url="/login?try_again" />
    <http-basic />
    <logout logout-url="/logout" delete-cookies="JSESSIONID" />
    <session-management invalid-session-url="/timeout" />

    <intercept-url pattern="/login*"    access="IS_AUTHENTICATED_ANONYMOUSLY" />

    ...

</http>

But instead of being deleted, the cookie is just became duplicated:

但 cookie 并没有被删除,而是被复制了:

Old cookie

老饼干

New cookie

新饼干

So it keeps redirecting the browser to the "/timeout" URL.

因此它不断将浏览器重定向到“/timeout” URL。

I tried to trace what's going on using the Developer Tools in Chrome web browser, and I found out that this cookie sets up with this response header:

我尝试使用 Chrome 网络浏览器中的开发人员工具跟踪发生了什么,我发现这个 cookie 设置了这个响应头:

Set-Cookie:JSESSIONID=CFF85EA743724F23FDA0317A75CFAD44; Path=/website/; HttpOnly

And deletes with this response header:

并使用此响应标头删除:

Set-Cookie:JSESSIONID=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/website

I'm not sure, but it seems like the reason is in the "Path" field of these headers: in the first one it points to "/website/", and in the second one it points to "/website".

我不确定,但似乎原因在这些标题的“路径”字段中:在第一个中它指向“/website/”,在第二个中它指向“/website”。

Is it the reason of the described trouble? If it's not the reason (or not the only reason), what is the other reason(s)? How should I fix this trouble?

是描述故障的原因吗?如果这不是原因(或不是唯一原因),那么其他原因是什么?我该如何解决这个问题?

回答by zagyi

You don't need to explicitly delete the JSESSIONIDcookie like this. It is not managed by Spring Security as such, but by your servlet container. Spring Security will by default invalidate the http session upon logout, which in turn causes your servlet container to remove the JSESSIONIDcookie.

您不需要JSESSIONID像这样显式删除cookie。它不是由 Spring Security 管理,而是由您的 servlet 容器管理。默认情况下,Spring Security 会在注销时使 http 会话无效,这反过来会导致您的 servlet 容器删除JSESSIONIDcookie。

回答by NotGaeL

In my case for some reason even though SecurityContextLogoutHandlercalls session.invalidate()JSESSIONIDwouldn't be cleared. Its value remained the same.

在我的情况下,由于某种原因,即使SecurityContextLogoutHandler调用session.invalidate()JSESSIONID也不会被清除。它的价值保持不变。

I tried to use delete-cookies="JSESSIONID"the same way the OP tried, and I believe I had the same problem: The path set for the cookie was the context path without a /at the end, so it still wouldn't be cleared (It was giving the order to delete a cookie that didn't exist).

我尝试使用delete-cookies="JSESSIONID"OP 尝试过的相同方式,我相信我遇到了同样的问题:为 cookie 设置的路径/是末尾没有 a 的上下文路径,所以它仍然不会被清除(它给出了命令删除不存在的cookie)。

I ended up writing my own ProperCookieClearLogoutHandler, which is identic to CookieClearLogoutHandlerexcept for the line that sets the context path for the cookie:

我最终编写了自己的ProperCookieClearLogoutHandler,它与CookieClearLogoutHandler相同,除了设置 cookie 的上下文路径的行:

package com.testdomain.testpackage;

import java.util.Arrays;
import java.util.List;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

public final class ProperCookieClearingLogoutHandler implements LogoutHandler {
    private final List<String> cookiesToClear;

    public ProperCookieClearingLogoutHandler(String... cookiesToClear) {
        Assert.notNull(cookiesToClear, "List of cookies cannot be null");
        this.cookiesToClear = Arrays.asList(cookiesToClear);
    }

    public void logout(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) {
        for (String cookieName : cookiesToClear) {
            Cookie cookie = new Cookie(cookieName, null);
            String cookiePath = request.getContextPath() + "/";
            if (!StringUtils.hasLength(cookiePath)) {
                cookiePath = "/";
            }
            cookie.setPath(cookiePath);
            cookie.setMaxAge(0);
            response.addCookie(cookie);
        }
    }
}

Then I set the config for the LogoutFilteron spring-security.xmlthis way;

然后我以这种方式设置LogoutFilter的配置spring-security.xml

    <bean id="logoutFilter"
        class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <constructor-arg name="logoutSuccessUrl" value="/views/login/login.xhtml?logout" />
        <constructor-arg>
            <list>
                <bean id="properCookieClearingLogoutHandler"
                    class="com.imatia.arpad.gplenos.authorization.ProperCookieClearingLogoutHandler">
                    <constructor-arg name="cookiesToClear">
                        <list>
                            <value>JSESSIONID</value>
                        </list>
                    </constructor-arg>
                </bean>
                <bean id="securityContextLogoutHandler"
                    class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
                </bean>
            </list>
        </constructor-arg>
        <property name="filterProcessesUrl" value="/logout" />
    </bean>

回答by srikanth vadapalli

The default CookieClearingLogoutHandler provided by spring could not clear JSESSIONID due to a difference in cookie path.

由于cookie路径不同,spring默认提供的CookieClearingLogoutHandler无法清除JSESSIONID。

You should not change the path of cookie. This would change the cookie identity. If the cookie were set for a path like /foo and you change this to /, then the client won't associate the changed cookie with the original cookie anymore. A cookie is identified by the name and the path.

您不应更改 cookie 的路径。这将更改 cookie 身份。如果 cookie 是为 /foo 之类的路径设置的,而您将其更改为 /,则客户端将不再将更改后的 cookie 与原始 cookie 相关联。cookie 由名称和路径标识。

Therefore you need to implement a custom CookieClearingLogoutHandler as shown in the above solution i.e (ProperCookieClearingLogoutHandler.class) and set it to spring security as shown in below code .Instead of using .deleteCookies("JSESSIONID","USER") which adds CookieClearingLogoutHandler by default.

因此,您需要实现一个自定义的 CookieClearingLogoutHandler,如上面的解决方案中所示,即 (ProperCookieClearingLogoutHandler.class) 并将其设置为 spring 安全性,如下面的代码所示。而不是使用 .deleteCookies("JSESSIONID","USER") 来添加 CookieClearingLogoutHandler默认。

Spring Security Java config:

Spring Security Java 配置:

@Configuration
@EnableWebSecurity
@ComponentScan(basePackages = "com.dentist.webapp")
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SessionRegistry sessionRegistry;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/resources/**", "/signup/*", "/about", "/login/*").permitAll().anyRequest()
                .authenticated()
                .and().formLogin()
                                 .loginPage("/login/form")
                                 .permitAll()
                .and().logout()
                              .invalidateHttpSession(true)
                              .clearAuthentication(true)
                             // .deleteCookies("JSESSIONID","USER")
                              .addLogoutHandler(new ProperCookieClearingLogoutHandler("JSESSIONID","USER"))
                              .permitAll()
                .and().sessionManagement()
                              .maximumSessions(1)
                              .maxSessionsPreventsLogin(true)
                              .expiredUrl("/accessDenied")
                              .sessionRegistry(sessionRegistry);

        }

    }

回答by pgardunoc

This is how I invalidate sessions:

这就是我使会话无效的方式:

<security:logout invalidate-session="true" logout-success-url="/myapp/auth/login" logout-url="/myapp/auth/logout" />

回答by Vladimir Nabokov

How to delete on log-out is straightforward: you implement logout and put that code in the method:

如何在注销时删除很简单:您实现注销并将该代码放入方法中:

    HttpSession session = request.getSession(false); 
    if (session != null) { 
       session.invalidate();
    }

Invalidated session will make the cookie invalid.

无效的会话将使 cookie 无效。

But I tried and found, that when I close the browser without logout, the JSESSIONID cookie survives and user able enter the system , when opens the browser.

但是我尝试发现,当我在没有注销的情况下关闭浏览器时,JSESSIONID cookie 仍然存在并且用户可以在打开浏览器时进入系统。

To eliminate this I created a filter, that invalidates the session on login too, creating a new session, where your login will be performed from start.

为了消除这种情况,我创建了一个过滤器,它也会在登录时使会话无效,创建一个新会话,您的登录将从一开始就执行。

@WebFilter(urlPatterns = {"/login/*"}, description = "sessionKiller", filterName="sessionKiller")
public class SessionKillerFilter implements Filter{

    @Override
    public void init(FilterConfig arg0) throws ServletException {}

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        //kill older session and create new one on explicit login
        //this is to prevent user to login 2-ce
        //also this is prevention of re-connect on cookie base, when browser closed and then open
        HttpServletRequest  request = (HttpServletRequest)req;
        HttpSession session =   request.getSession(false);
        if(session!=null){
            session.invalidate();//old session invalidated
        }
        request.getSession(true);//new session created

        chain.doFilter(req, resp);
    }

    @Override
    public void destroy() {}
}