Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/356.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 会话isn';t在Spring Security中手动过期(失效)后立即销毁_Java_Spring_Spring Security - Fatal编程技术网

Java 会话isn';t在Spring Security中手动过期(失效)后立即销毁

Java 会话isn';t在Spring Security中手动过期(失效)后立即销毁,java,spring,spring-security,Java,Spring,Spring Security,我正在编写一个游戏(web应用程序)的代码,我想在其中实现以下策略。允许用户登录一次。第二次登录会导致警告消息和选择:离开或坚持登录。如果用户选择登录,则其以前的所有会话都将过期。得益于Spring安全设施,它运行良好: 公共类安全配置扩展了WebSecurity配置适配器{ @凌驾 受保护的无效配置(HttpSecurity http)引发异常{ ... .formLogin() .failureHandler(新的SecurityErrorHandler()) .及() .会议管理() .最

我正在编写一个游戏(web应用程序)的代码,我想在其中实现以下策略。允许用户登录一次。第二次登录会导致警告消息和选择:离开或坚持登录。如果用户选择登录,则其以前的所有会话都将过期。得益于Spring安全设施,它运行良好:

公共类安全配置扩展了WebSecurity配置适配器{
@凌驾
受保护的无效配置(HttpSecurity http)引发异常{
...
.formLogin()
.failureHandler(新的SecurityErrorHandler())
.及()
.会议管理()
.最多会议(1)
.maxSessionsPreventsLogin(真)
…

如果由于maxSessionsPreventsLogin(true)而导致第二次登录,将引发异常。然后SecurityErrorHandler将捕获该异常,并执行重定向到警告页面的操作:

public class SecurityErrorHandler extends SimpleUrlAuthenticationFailureHandler {

@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
    if (exception.getClass().isAssignableFrom(SessionAuthenticationException.class)) {
        request.getRequestDispatcher("/double_login/"+request.getParameterValues("username")[0]).forward(request, response);   //...the rest of the method
到目前为止,一切正常。如果尽管警告用户选择第二次登录(他可能在第一次会话无法管理的情况下关闭了浏览器而没有注销),他按下一个按钮,然后控制器调用特殊服务,以使该用户的前一次会话过期(使用他的登录用户名):

此外,还有一个SessionEventListener(由于注销、自然过期或“information.expireNow()”强制过期而无所谓),它观察会话破坏事件并实现特定逻辑,例如保存用户的持久数据、清除缓存等)。此逻辑至关重要。执行此逻辑的代码如下:

public class SessionEventListener extends HttpSessionEventPublisher {

@Override
public void sessionCreated(HttpSessionEvent event) {
    super.sessionCreated(event);
    event.getSession().setMaxInactiveInterval(60*3);
}

@Override
public void sessionDestroyed(HttpSessionEvent event) {
    String name=null;
    SessionRegistry sessionRegistry = getSessionRegistry(event);
    SessionInformation sessionInfo = (sessionRegistry != null ? sessionRegistry
            .getSessionInformation(event.getSession().getId()) : null);
    UserDetails ud = null;
    if (sessionInfo != null) {
        ud = (UserDetails) sessionInfo.getPrincipal();
    }
    if (ud != null) {
        name=ud.getUsername();
        //OUR IMPORTANT ACTIONS IN CASE OF SESSION CLOSING 
        getAllGames(event).removeByName(name);
    }
    super.sessionDestroyed(event);
}



public AllGames getAllGames(HttpSessionEvent event){
    HttpSession session = event.getSession();
    ApplicationContext ctx =
            WebApplicationContextUtils.
                    getWebApplicationContext(session.getServletContext());
    return (AllGames) ctx.getBean("allGames");
}

public SessionRegistry getSessionRegistry(HttpSessionEvent event){
    HttpSession session = event.getSession();
    ApplicationContext ctx =
            WebApplicationContextUtils.
                    getWebApplicationContext(session.getServletContext());
    return (SessionRegistry) ctx.getBean("sessionRegistry");
}
}

然后地狱就发生了。尽管我期望“sessionDestroyed”方法的事件不会在会话到期后立即发生,而是在用户第二次登录后才发生(这是允许的,因为他的上一个会话在那一刻已过期,但令我惊讶的是,Spring Security直到现在都没有破坏上一个会话)。因此,从“sessionDestroyed”调用的服务“getAllGames(event).removeByName(name)”中实现的逻辑发生得太晚,更糟糕的是,在用户第二次登录之后。它破坏了逻辑

我可以实施不同的解决方法,比如拐杖。但是,如果有人知道如何直接解决它,我希望你能给我一些建议

备注。我已经调用了“session.invalidate()”,但也没有用

SessionEventListener中的“sessionDestroyed”被及时触发(会话到期后立即触发),这对于已经体现的逻辑非常重要。坦白说,我不知道如何以正确和直接的方式实现它


非常感谢您的帮助和建议。

我自己找到的唯一答案是:在会话中发送下一个HTTP请求之前,过期会话仍然不会被销毁。因此,要终止过期会话,您需要代表会话模拟此类请求。我通过创建并调用followin来完成此操作g方法:

 void killExpiredSessionForSure(String sessionID) {

            //sessionID - belongs to a session you want to kill
            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.add("Cookie", "JSESSIONID=" + sessionID);
            HttpEntity requestEntity = new HttpEntity(null, requestHeaders);
            RestTemplate rt = new RestTemplate();
            rt.exchange("http://localhost:8080", HttpMethod.GET, requestEntity, String.class);          
 }
调用此方法后,将正确发出和处理事件“sessionDestroyed”,并且过期的会话不再存在

 void killExpiredSessionForSure(String sessionID) {

            //sessionID - belongs to a session you want to kill
            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.add("Cookie", "JSESSIONID=" + sessionID);
            HttpEntity requestEntity = new HttpEntity(null, requestHeaders);
            RestTemplate rt = new RestTemplate();
            rt.exchange("http://localhost:8080", HttpMethod.GET, requestEntity, String.class);          
 }