1
votes

I'm developing a webapp with Java + Spring MVC + Hibernate + Spring Security 3.1. When I log out instead of just redirect to the log in page it goes to the session expired method so it shows the log in page but with a "Session expired!" message...

Here's 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>

The log out button calls:

@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";
    }
}

I tried commenting out those lines and using them, but the behaviour is exactly the same... Constantes.USUARIO_SESION stores the name of the user variable in session.

The log in method executes, among other things:

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);

Session expired goes through:

@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";
}

And 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>

I tested it on Tomcat 6 and WAS 8.5...

EDIT: If I get rid of Spring Security's logout and implement my own it works as expected:

I erase: <security:logout logout-success-url="/" delete-cookies="JSESSIONID"/> from security-context.xml and change the method that is called on logout:

@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";
    }

Why is it working now? These lines of code are taken from Spring's logout code...

2

2 Answers

1
votes

You need add

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

to you security:http section.

0
votes

I don't understand what you are trying to accomplish. You wrote your own controller that invalidates the session then redirects to the spring security logout url. The controller is unnecessary, just use the spring logout url directly, by default it will invalidate the session for you. If you need to add special behavior on logout, either write your own LogoutSuccessHandler or extend one of the spring handlers and add it to the LogoutFilter.