【问题标题】:Spring Security 3.1: after logging-out catches session expiredSpring Security 3.1:注销后捕获会话过期
【发布时间】:2012-09-25 08:16:10
【问题描述】:

我正在使用 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 中删除:&lt;security:logout logout-success-url="/" delete-cookies="JSESSIONID"/&gt; 并更改注销时调用的方法:

@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 的注销代码……

【问题讨论】:

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


    【解决方案1】:

    你需要添加

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

    给你security:http 部分。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 仅用于测试目的...即使控制器仅重定向到 Spring 注销它也无法正常工作,我的意思是,您单击注销按钮,Spring 执行它的操作,然后它被转发到会话过期过滤器......所以当我尝试注销时,我最终进入登录页面但“会话过期!”警告...这是我试图避免但无法完成的事情,这就是我编写控制器的原因,如果我不转发到 Spring 的注销并执行我自己的代码,它就可以工作...
      猜你喜欢
      • 1970-01-01
      • 2015-06-30
      • 2017-05-16
      • 2017-06-11
      • 2018-06-14
      • 2018-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多