Spring Security 3.1:登出后捕获会话已过期

时间:2012-09-25 08:16:10

标签: spring spring-mvc spring-security logout session-timeout

我正在使用Java + Spring MVC + Hibernate + Spring Security 3.1开发一个webapp。当我退出而不是仅仅重定向到登录页面时,它转到会话过期的方法,因此它显示登录页面,但是“会话已过期!”消息......

这是security-context.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <security:debug />

    <!-- preauthentication -->    
    <security:global-method-security pre-post-annotations="enabled">
    </security:global-method-security>

   <security:http auto-config="false" use-expressions="true" entry-point-ref="http403EntryPoint" access-denied-page="/errores/accesodenegado">
        <security:intercept-url pattern="/" access="permitAll"/>
        <security:intercept-url pattern="/error.jsp" access="permitAll"/>
        <!-- Allow non-secure access to static resources  -->
        <security:intercept-url pattern="/resources/**" access="permitAll"/>
        <security:intercept-url pattern="/autenticacion/**" access="permitAll"/>
        <security:intercept-url pattern="/errores/**" access="permitAll"/>
        <!-- URLs que dependen de perfiles -->
        <security:intercept-url pattern="/gestion/facturas/**" access="hasAnyRole('ROLE_ADMIN','ROLE_S_CEN','ROLE_CONSL')"/>
        <security:intercept-url pattern="/gestion/tarifas/**" access="hasAnyRole('ROLE_ADMIN','ROLE_S_CEN','ROLE_CONSL')"/>
        <security:intercept-url pattern="/gestion/envios/**" access="hasAnyRole('ROLE_ADMIN','ROLE_S_CEN')"/>
        <security:intercept-url pattern="/gestion/perfiles/**" access="hasRole('ROLE_ADMIN')"/>
        <security:intercept-url pattern="/gestion/usuarios/**" access="hasRole('ROLE_ADMIN')"/>
        <security:intercept-url pattern="/consulta/**" access="hasAnyRole('ROLE_CONSL','ROLE_ADMIN','ROLE_S_CEN')"/>
        <security:intercept-url pattern="/importacion/**" access="hasAnyRole('ROLE_ADMIN','ROLE_S_CEN')"/>
        <!-- Pantalla a la que redirige el logout -->           
        <security:logout logout-success-url="/" delete-cookies="JSESSIONID"/>
        <!-- El session timeout lleva a la pantalla de login -->
        <security:session-management invalid-session-url="/errores/sesionexpirada" />
    </security:http>

    <bean id="http403EntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint">
    </bean>

    <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
        <security:filter-chain-map path-type="ant">
            <security:filter-chain pattern="/**" filters="j2eePreAuthFilter"/>
        </security:filter-chain-map>
    </bean>

    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider ref='preAuthenticatedAuthenticationProvider'/>
    </security:authentication-manager>

    <bean id="preAuthenticatedAuthenticationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
        <property name="preAuthenticatedUserDetailsService" ref="preAuthenticatedUserDetailsService"/>
    </bean>

    <bean id="preAuthenticatedUserDetailsService"
            class="org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService"/>


    <bean id="j2eePreAuthFilter" class="es.myApp.security.MyAppUserJ2eePreAuthenticatedProcessingFilter">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationDetailsSource" ref="authenticationDetailsSource"/>
        <property name="continueFilterChainOnUnsuccessfulAuthentication" value="false"/>
    </bean>

    <bean id="authenticationDetailsSource" class="org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource">
        <property name="mappableRolesRetriever" ref="j2eeMappableRolesRetriever"/>
        <property name="userRoles2GrantedAuthoritiesMapper" ref="j2eeUserRoles2GrantedAuthoritiesMapper"/>
    </bean>

    <bean id="j2eeMappableRolesRetriever" class="org.springframework.security.web.authentication.preauth.j2ee.WebXmlMappableAttributesRetriever">
    </bean>

    <bean id="j2eeUserRoles2GrantedAuthoritiesMapper" class="org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper">
        <property name="attributePrefix" value="test"/>
    </bean>

</beans>

注销按钮调用:

@Controller
@RequestMapping("/autenticacion")
public class AutenticacionController {

[...]

@RequestMapping(value = "salir")
    public String salir(Model model, HttpServletRequest request, HttpServletResponse response) {        
//      request.getSession().removeAttribute(Constantes.USUARIO_SESION);
//      request.getSession().invalidate();
        return "redirect:/j_spring_security_logout";
    }
}

我尝试评论这些行并使用它们,但行为完全相同... Constantes.USUARIO_SESION在会话中存储用户变量的名称。

登录方法执行以下操作:

request.getSession().setAttribute(Constantes.USUARIO_SESION, usuario);

UserDetails userDetails = myAppUserDetailsService.loadUserByUsername(usuario.getLogin());
Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                            SecurityContextHolder.getContext().setAuthentication(auth);

会话已过期:

@RequestMapping("sesionexpirada")
    public String sesionExpirada(Model model, HttpServletRequest request, HttpServletResponse response) {
    MessageManager msgManager = new MessageManager();

    msgManager.addError("error.sesion.expirada");
    request.getSession().setAttribute("messageManager", msgManager);

    return "inicio";
}

和web.xml

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Aplicación Web MyApp</display-name>

    <!-- Define la localización de los ficheros de configuración de Spring -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/classes/applicationContext.xml
        </param-value>
    </context-param>

    <!-- Reads request input using UTF-8 encoding -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>myAppUserJ2eePreAuthenticatedProcessingFilter</filter-name>
        <filter-class>es.myApp.security.XiscoUserJ2eePreAuthenticatedProcessingFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>myAppUserJ2eePreAuthenticatedProcessingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Handles all requests into the application -->
    <servlet>
        <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
        <servlet-class>es.myApp.controller.XiscoDispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- del. welcome files -->
    <!-- useful for Servlet 3 container (Tomcat 7 and Jetty 6) -->
    <welcome-file-list>
        <welcome-file></welcome-file>
    </welcome-file-list>

    <!-- Página de error -->
    <error-page>
        <error-code>404</error-code>
        <location>/errores/error</location>
    </error-page>

    <!-- Tiempo de sesión -->
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>

    <listener>
        <listener-class>
          org.springframework.security.web.session.HttpSessionEventPublisher
        </listener-class>
    </listener>

    <!-- Referencia a recursos jndi WAS -->
    <resource-ref id="ResourceRef_MyApp>
        <res-ref-name>jdbc/myApp</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
        <res-sharing-scope>Shareable</res-sharing-scope>
    </resource-ref>

</web-app>

我在Tomcat 6和WAS 8.5上测试过它......

编辑:如果我摆脱Spring Security的注销并实现自己的注销,它会按预期工作:

我从security-context.xml中删除:<security:logout logout-success-url="/" delete-cookies="JSESSIONID"/>并更改注销时调用的方法:

@RequestMapping("salir")
    public String salir(Model model, HttpServletRequest request, HttpServletResponse response) {        
        request.getSession().removeAttribute(Constantes.USUARIO_SESION);

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

        return "inicio";
    }

为什么现在有效?这些代码行取自Spring的注销代码......

2 个答案:

答案 0 :(得分:1)

您需要添加

 <security:session-management session-fixation-protection="none"/>

给你security:http部分。

答案 1 :(得分:0)

我不明白你想要完成什么。您编写了自己的控制器,使会话无效,然后重定向到spring security logout url。控制器是不必要的,只需直接使用spring logout url,默认情况下它会使会话无效。如果需要在注销时添加特殊行为,请编写自己的LogoutSuccessHandler或扩展其中一个弹簧处理程序并将其添加到LogoutFilter。