spring 注销会在浏览器上留下 JSESSIONID。如何清除?

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

Logout leaves behind JSESSIONID on the browser. How to clear it?

springspring-mvcspring-securitylogoutjsessionid

提问by Shiv Kumar Ganesh

I am using the following code for logging out a user off my system.

我正在使用以下代码将用户从我的系统中注销。

/**
 * This function helps to set the session attribute for the present user to null and then
 * removes the attribute itself and this helps in clearing the session
 * @param request
 * @param response
 */
@RequestMapping(value = AuthConstants.EXIT, method = RequestMethod.POST)
public void exitPrime(HttpServletRequest request, HttpServletResponse response) {
        /*Getting session and then invalidating it*/
        HttpSession session = request.getSession(false);
        if(request.isRequestedSessionIdValid() && session != null)
        {
            session.invalidate();

        }
}

This leads to a successful logout but the JSESSION ID given by the while logging in still remains in the browser due to which for any new user the same JSESSION ID is used again while Logging In. I want the JSESSIONID cookie to be valid only for the current session and once the user logs out, it should be destroyed or invalid for the login done for the next time. My Login Code is as follows :-

这会导致成功注销,但登录时给出的 JSESSION ID 仍保留在浏览器中,因为对于任何新用户,登录时都会再次使用相同的 JSESSION ID。我希望 JSESSIONID cookie 只对当前会话有效,一旦用户注销,它应该被销毁或对下次登录无效。我的登录代码如下:-

/**
 * This method allows one to log into the system and generates a token for a valid employee.
 * @param authRequest
 * @param request
 * @param response
 * @return
 */
@RequestMapping(value = AuthConstants.ENTRY, method = RequestMethod.POST, consumes = ApplicationConstants.APPLICATION_JSON)
public @ResponseBody
AuthResponse primeEntry(@RequestBody AuthRequest authRequest,HttpServletRequest request, HttpServletResponse response) {
    AuthResponse authResponse = new AuthResponse();
    if(authRequest != null && authRequest.getEmployeeAuth().getEmployeeNumber() != null 
            && !authRequest.getEmployeeAuth().getEmployeeNumber().isEmpty()){
        /*To check whether the user is valid*/
        String employeeNumber = authRequest.getEmployeeAuth().getEmployeeNumber();
        UserBean userBean = new UserBean();
        userBean = userService.getUser(employeeNumber);
        if(userBean != null)
            {
            HttpSession session = request.getSession(true);
            session.setAttribute("user", userBean);
            setAuthResponseSuccess(authResponse);
        }else{
            /*If user does not exist the too throw error 500*/
            setAuthResponseFailure(authResponse);
        }
    }else{
        /*If input JSON is not valid then throw error 500*/
        setAuthResponseFailure(authResponse);
    }
    return authResponse;
}

I am using Spring 3.2 and want to do Login and Logout Manually. Please Help.

我正在使用 Spring 3.2 并希望手动登录和注销。请帮忙。

Full Class Code

全班代码

@Controller
@RequestMapping(value = "/auth")
public class AuthController {
    @Autowired
    HttpServletRequest request;

    @Autowired
    HttpSession session;

    @Autowired
    IUserService userService;

    /**
     * This method allows one to log into the system and generates a token for a valid employee.
     * @param authRequest
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(value = AuthConstants.ENTRY, method = RequestMethod.POST, consumes = ApplicationConstants.APPLICATION_JSON)
    public @ResponseBody
    AuthResponse primeEntry(@RequestBody AuthRequest authRequest,HttpServletRequest request, HttpServletResponse response) {
        AuthResponse authResponse = new AuthResponse();
        if(authRequest != null && authRequest.getEmployeeAuth().getEmployeeNumber() != null 
                && !authRequest.getEmployeeAuth().getEmployeeNumber().isEmpty()){
            /*To check whether the user is valid*/
            String employeeNumber = authRequest.getEmployeeAuth().getEmployeeNumber();
            UserBean userBean = new UserBean();
            userBean = userService.getUser(employeeNumber);
            if(userBean != null)
                {
                HttpSession session = request.getSession(true);
                session.setAttribute("user", userBean);
                setAuthResponseSuccess(authResponse);
            }else{
                /*If user does not exist the too throw error 500*/
                setAuthResponseFailure(authResponse);
            }
        }else{
            /*If input JSON is not valid then throw error 500*/
            setAuthResponseFailure(authResponse);
        }
        return authResponse;
    }


    /**
     * This function helps to set the session attribute for the present user to null and then
     * removes the attribute itself and this helps in clearing the session
     * @param request
     * @param response
     */
    @RequestMapping(value = AuthConstants.EXIT, method = RequestMethod.POST)
    public void exitPrime(HttpServletRequest request, HttpServletResponse response) {
            /*Getting session and then invalidating it*/
            HttpSession session = request.getSession(false);
            if(request.isRequestedSessionIdValid() && session != null)
            {
                session.invalidate();

            }
    }

    private AuthResponse setAuthResponseFailure(AuthResponse authResponse) {
        authResponse.setResponseCode(ApplicationConstants.INTERNAL_ERROR_CODE);
        authResponse.setStatus(StatusType.FAILURE);
        authResponse.setResponseMsg(ApplicationConstants.INTERNAL_ERROR_MESSAGE);
        return authResponse;
    }
    private AuthResponse setAuthResponseSuccess(AuthResponse authResponse){
        authResponse.setResponseCode(ApplicationConstants.OK);
        authResponse.setStatus(StatusType.SUCCESS);
        authResponse.setResponseMsg(ApplicationConstants.LOGIN_SUCCESS);
        return authResponse;
    }
}

回答by Shiv Kumar Ganesh

After experimenting a bit I reached to a conclusion that if you want the browser cookie value to persist then just dont do anything and the above code would work fine for you. On the other hand if you want the output of the cookie something like

经过一些试验,我得出的结论是,如果您希望浏览器 cookie 值保持不变,那么只要不做任何事情,上面的代码就可以正常工作。另一方面,如果您想要 cookie 的输出类似于

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

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

Then you can take this code snippet and try it out.

然后你可以拿这个代码片段试一试。

private void handleLogOutResponseCookie(HttpServletResponse response) {
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            cookie.setMaxAge(0);
            cookie.setValue(null);
            cookie.setPath("/");
            response.addCookie(cookie);
        }

This would solve the problem and destroy the cookie while you logout.

这将解决问题并在您注销时销毁 cookie。

回答by gerrytan

There's nothing wrong with JSESSIONID leftover on your browser as long as it's already invalid. JSESSIONID is just a bunch of random characters that don't contain your actual data.

只要 JSESSIONID 已经无效,浏览器上剩余的 JSESSIONID 就没有问题。JSESSIONID 只是一堆不包含您实际数据的随机字符。

However I suspect your problem is you used @SessionAttributesannotation at class level, and you attempted session.invalidate(). With this scenario after the previous session is invalidated, Spring automatically creates a new session (and JSESSIONID) for youbecause it has to persist specified model attributes into session.

但是我怀疑您的问题是您@SessionAttributes在类级别使用了注释,并且您尝试了session.invalidate(). 在上一个会话失效后的这种情况下,Spring 会自动为您创建一个新会话(和 JSESSIONID),因为它必须将指定的模型属性持久化到会话中。

IMO a better approach is to create a new controller that does not have @SessionAttributesand invalidate your session from there.

IMO 更好的方法是创建一个新的控制器,该控制器没有@SessionAttributes并从那里使您的会话无效。

回答by Igor Grunskiy

Not sure if it still actual, but one can extend LogoutFilter like this to specify exact steps to be made on logout, including custom cookies invalidation.

不确定它是否仍然存在,但可以像这样扩展 LogoutFilter 以指定注销时要执行的确切步骤,包括自定义 cookie 失效。

<beans:bean id="sessionInvalidationFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
    <beans:property name="filterProcessesUrl" value="/logout"/>
    <beans:constructor-arg>
        <beans:array>
            <beans:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
            <beans:bean class="org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler">
                <beans:constructor-arg value="JSESSIONID"/>
            </beans:bean>
        </beans:array>
    </beans:constructor-arg>
</beans:bean>

回答by Santosh

One way I could think of is to delete the JSESSIONIDcookie on logout action. The way to delete the cookie is to set its age to zero as follows.

我能想到的一种方法是JSESSIONID在注销操作时删除cookie。删除cookie的方法是将其年龄设置为零,如下所示。

Cookie cookie = new Cookie();
cookie.setValue(null);
cookie.setMaxAge(0);
cookie.setPath("/");

Here I have added the path as root. Please check JSESSIONIDcookie in your browser for the correct path.

在这里,我以 root 身份添加了路径。请JSESSIONID在您的浏览器中检查cookie 以获取正确的路径。

Once you have this, add this to the response

一旦你有了这个,把它添加到响应中

response.addCookie(cookie);

You can put this code in your exitPrime()method.

您可以将此代码放在您的exitPrime()方法中。

回答by J Gibbs

The approach listed previously didn't work for me, but with some modification I got it to work, I've only done limited testing though so YMMV.

前面列出的方法对我不起作用,但是经过一些修改后我让它起作用了,尽管如此,我只做了有限的测试,所以 YMMV。

protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
  HttpSession session = req.getSession(false);
  if (session != null) {
    String sessionId = session.getId();
    session.invalidate();
    Cookie[] cookies = req.getCookies();
    for (Cookie cookie : cookies) {
      if (sessionId.equalsIgnoreCase(cookie.getValue())) {
        cookie.setMaxAge(0);
        cookie.setValue(null);
        cookie.setDomain(req.getServerName());
        cookie.setPath(req.getServletContext().getContextPath() + "/");
        cookie.setSecure(req.isSecure());
        res.addCookie(cookie);
        break;
      }
    }
  }
}

回答by Debasmita Sahoo

Tomcat appends a slash at the end of the context path. Now, when you set the delete-cookie attribute, Spring tries to find the cookie for the path without a slash at the end. Because it doesn't find it, the cookie will not be removed, resulting in the display of session expiration page instead of login page.

Tomcat 在上下文路径的末尾附加一个斜杠。现在,当您设置 delete-cookie 属性时,Spring 会尝试查找末尾不带斜杠的路径的 cookie。因为没有找到,cookie不会被删除,导致显示session过期页面而不是登录页面。

Following workaround will do the trick.

以下解决方法可以解决问题。

public void logout(HttpServletRequest request, HttpServletResponse response,
                    Authentication auth) {
    Cookie cookieWithSlash = new Cookie("JSESSIONID", null);
    //Tomcat adds extra slash at the end of context path (e.g. "/foo/")
    cookieWithSlash.setPath(request.getContextPath() + "/"); 
    cookieWithSlash.setMaxAge(0); 

    Cookie cookieWithoutSlash = new Cookie("JSESSIONID", null);
    //JBoss doesn't add extra slash at the end of context path (e.g. "/foo")
    cookieWithoutSlash.setPath(request.getContextPath()); 
    cookieWithoutSlash.setMaxAge(0); 

    //Remove cookies on logout so that invalidSessionURL (session timeout) is not displayed on proper logout event
    response.addCookie(cookieWithSlash); //For Tomcat 
    response.addCookie(cookieWithoutSlash); //For JBoss
}