Spring安全性会话是在会话超时后无需登录的情况下创建的

时间:2014-12-12 18:12:16

标签: spring-mvc spring-security

我创建了默认的表单登录身份验证,下面是我的配置。                    

<http auto-config="true" use-expressions="true">
    <request-cache ref="authenticationRequestCache" />
    <access-denied-handler error-page="/rest/login?error=denied" />
    <form-login login-page="/rest/login"    
        authentication-success-handler-ref="successHandler"
        authentication-failure-url="/rest/login?error" />
    <intercept-url pattern="/rest/devices" access="denyAll" />
    <intercept-url pattern="/rest/devices/**" access="denyAll" />
    <intercept-url pattern="/rest/super/**" access="hasRole('ROLE_SUPER')" />
    <intercept-url pattern="/rest/**" access="isAuthenticated()" />
    <intercept-url pattern="/cavirinRest/*" />
    <session-management invalid-session-url="/rest/login?error=sessionExpired" 
        session-authentication-strategy-ref="sas" />
    <logout invalidate-session="true" logout-success-url="/rest/login" delete-cookies="JSESSIONID"/>
</http>

身份验证按预期正常工作,但最近我向应用程序添加了会话超时。我的应用程序每5秒轮询一次服务器,因此默认超时不起作用。我用Google搜索并在下面找到了带过滤器的解决方案并实现了它。

来自web.xml:

<filter>
    <filter-name>XhrSessionTimeoutFilter</filter-name>
    <filter-class>com.cavirin.security.filter.XhrSessionTimeoutFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>XhrSessionTimeoutFilter</filter-name>
    <url-pattern>/rest/*</url-pattern>
</filter-mapping>


<session-config>
    <!-- Disables URL-based sessions (no more 'jsessionid' in the URL using Tomcat) -->
    <tracking-mode>COOKIE</tracking-mode>
    <session-timeout>2</session-timeout>
</session-config>


@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

    HttpSession session = request.getSession(false);

    if (session != null) {
        logger.trace("doFilterInternal(): session object is not null.");

        session.setMaxInactiveInterval(120); //2 mins for testing //set max inactive interval to 30 mins

        // if requestURI is not null
        if (request.getRequestURI() != null) {

            logger.trace("doFilterInternal(): request.getRequestURI() : {} ", request.getRequestURI());

            String ajaxHeader = request.getHeader("X-Requested-With");
            logger.trace("doFilterInternal(): ajaxHeader : {} ", ajaxHeader);

            //if it is an AJAX call
            if ("XMLHttpRequest".equals(ajaxHeader)) {
                logger.trace("doFilterInternal(): An AJAX call, set the last access time, if not already set.");
                Long lastAccess = (Long) session.getAttribute(AJAX_DATA_LAST_ACCESS_TIME);

                if (lastAccess == null) {
                    logger.trace("doFilterInternal(): Last access time is null, set current time as lastAccess time.");
                    lastAccess = System.currentTimeMillis();
                    session.setAttribute(AJAX_DATA_LAST_ACCESS_TIME, lastAccess);
                } else {
                    logger.trace("doFilterInternal(): max interval: {} -- lastAccess: {} -- currentTimeMillis: {} ",
                        + session.getMaxInactiveInterval(), lastAccess, System.currentTimeMillis());
                    if (((session.getMaxInactiveInterval() * 1000) - (System.currentTimeMillis() - lastAccess)) < 0) {
                        logger.debug("doFilterInternal(): session should be invalidated as inative time execeeded.");
                        session.invalidate();
                    }
                }
            } else {
                logger.trace("doFilterInternal(): Not an AJAX call.");
                session.removeAttribute(AJAX_DATA_LAST_ACCESS_TIME);
            }
        }
    }

    filterChain.doFilter(request, response);
}

Timeout也可以正常工作,但是一旦超时,我的ajax请求(我在chrome上看到它 - 开发人员工具,网络选项卡)在重定向到另一个页面时会获得302状态。即我的登录页面,其中包含会话超时错误消息。我没有点击任何东西,我再离开它2分钟(我设置2分钟超时进行测试),我再次看到一条日志消息,说明&#34;会话已过期&#34;并重定向到登录。我可以看到在会话到期后创建了一个新的jsessionid

    RequestMapping(method = RequestMethod.GET)
public ModelAndView showLogin(Model model,
                              @RequestParam(value = "error", required = false) String errorStr,
                              HttpServletRequest request) {
    logger.trace("Returning login page view.");

    String pageToBeLoaded = "rest/login" ;

    System.out.println("showLogin(): errorStr: " + errorStr);

    if (errorStr != null) {

        if (errorStr.equalsIgnoreCase("sessionExpired")) {
            logger.debug("showLogin(): error: sessionExpired ");
            System.out.println("showLogin(): error: sessionExpired");
            model.addAttribute("error", "Session expired. Please log in again.");
        } else if (errorStr.equalsIgnoreCase("denied")) {
            logger.debug("showLogin(): error: denied ");
            System.out.println("showLogin(): error: denied");
            model.addAttribute("error", "Access is denined. This page is for SUPER user only.");
        } else {
            model.addAttribute("error", getErrorMessage(request, "SPRING_SECURITY_LAST_EXCEPTION"));
            System.out.println("showLogin(): error: " + getErrorMessage(request, "SPRING_SECURITY_LAST_EXCEPTION"));
        }
    } 
    return new ModelAndView(pageToBeLoaded);
}

我知道我在超时后没有停止轮询服务器,所以它一次又一次地轮询,但是1)如何在不登录应用程序的情况下创建会话?

此外,2)我没有收到会话超时错误消息,因为它被重定向到此页面一次又一次进入登录页面。在用户再次登录之前,我无法保留此错误消息。

任何指针?

2 个答案:

答案 0 :(得分:1)

在阅读了关于春季安全及其相关内容的大量文章后,我们才知道这一点 1)会话默认创建(http config,if-required),一旦会话超时,它将自动重定向到invalid-session-url的会话管理上的给定URL并创建另一个会话。我在spring-security调试日志中观察到了这种行为,该日志声明会话无效,如果需要,创建另一个会话。 2)一旦超时会话失效,它将重定向到invalid-session-url;但我的ajax保持从同一页面轮询,创建另一个会话并查找访问我的安全网页的身份验证,因此重定向发生在我的登录页面,但这次没有会话超时错误。

答案 1 :(得分:1)

您正确找到了答案。

让我解释一下。

默认情况下会创建会话
是。它被称为ROLE_ANONYMOUS。

默认情况下,如果没有会话(没有带有JSESSIONID的cookie),它将创建一个Role = ROLE_ANONYMOUS的会话。如果已经通过身份验证,则不会创建新会话。

因此ROLE_ANONYMOUS是未经身份验证的用户。

permitAll也包括ANONYMOUS用户。匿名用户将被允许访问该资源。

只需尝试一下。像这样向匿名用户授权管理资源

.antMatchers("/app/admin/*").hasRole("ANONYMOUS")

然后只能在没有登录的情况下访问管理员资源。如果使用管理员凭据登录并尝试访问,则会收到403错误(禁止访问)。

未经授权的用户是一个匿名用户,他还将获得一个会话以标识他,因此,在超时后他的会话也将失效,因此它将重定向到在会话管理expiredUrl中配置的页面。

他是唯一无法访问AuthenticationSuccessHandler的用户