【问题标题】:Session timeout leads to Access Denied in Spring MVC when CSRF integration with Spring Security当 CSRF 与 Spring Security 集成时,会话超时导致 Spring MVC 中的访问被拒绝
【发布时间】:2015-02-23 14:14:18
【问题描述】:

我在我的 Spring MVC 项目中集成了 CSRF 令牌和 Spring Security。一切都与 CSRF 令牌一起正常工作,令牌将从客户端发送到服务器端。

我已经更改了我的 logout 进程,使其使用 POST 方法来发送 CSRF 令牌并且它工作正常。

发生会话超时时我遇到了问题,它需要重定向到 spring 默认注销 URL,但它在该 URL 上给了我Access Denied

如何覆盖此行为。

我在安全配置文件中包含以下行

   <http>
         //Other config parameters
        <csrf/>
   </http>

如果有人需要更多信息,请告诉我。

【问题讨论】:

    标签: spring spring-mvc spring-security csrf-protection


    【解决方案1】:

    mdrg 提供的答案是对的,我还实现了一个自定义的AccessDeniedHandler,我提交给你考虑:

    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.web.access.AccessDeniedHandlerImpl;
    import org.springframework.security.web.csrf.MissingCsrfTokenException;
    import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
    import org.springframework.security.web.savedrequest.RequestCache;
    
    /**
     * Intended to fix the CSRF Timeout Caveat 
     * (https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf-timeouts).
     * When the session expires and a request requiring CSRF is received (POST), the
     * missing token exception is handled by caching the current request and 
     * redirecting the user to the login page after which their original request will
     * complete. The intended result is that no loss of data due to the timeout will
     * occur.
     */
    public class MissingCsrfTokenAccessDeniedHandler extends AccessDeniedHandlerImpl {
      private RequestCache requestCache = new HttpSessionRequestCache();
      private String loginPage = "/login";
    
      @Override
      public void handle(HttpServletRequest req, HttpServletResponse res, AccessDeniedException exception) throws IOException, ServletException {
        if (exception instanceof MissingCsrfTokenException && isSessionInvalid(req)) {
          requestCache.saveRequest(req, res);
          res.sendRedirect(req.getContextPath() + loginPage);
        }
        super.handle(req, res, exception);
      }
    
      private boolean isSessionInvalid(HttpServletRequest req) {
        try {
          HttpSession session = req.getSession(false);
          return session == null || !req.isRequestedSessionIdValid();
        }
        catch (IllegalStateException ex) {
          return true;
        }
      }
    
      public void setRequestCache(RequestCache requestCache) {
        this.requestCache = requestCache;
      }
    
      public void setLoginPage(String loginPage) {
        this.loginPage = loginPage;
      }
    } 
    

    通过 java config 连接:

    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
      @Override
      protected void configure(HttpSecurity http) throws Exception {
        ...
        http.exceptionHandling().accessDeniedHandler(getAccessDeniedHandler());
        ...
      }
    
       public AccessDeniedHandler getAccessDeniedHandler() {
        return new MissingCsrfTokenAccessDeniedHandler();
      }
    }
    

    【讨论】:

      【解决方案2】:

      这个问题有点老了,但答案总是有用的。

      首先,这是会话支持的 CSRF 令牌的一个已知问题,如文档中所述:CSRF Caveats - Timeouts

      要解决这个问题,请使用一些 Javascript 来检测即将发生的超时,使用与会话无关的 CSRF 令牌存储库或创建自定义 AccessDeniedHandler 路由。我选择了后者:

      配置 XML:

      <http>
          <!-- ... -->
          <access-denied-handler ref="myAccessDeniedHandler"/>
      </http>
      
      <bean id="myAccessDeniedHandler" class="package.MyAccessDeniedHandler">
          <!-- <constructor-arg ref="myInvalidSessionStrategy" /> -->
      </bean>
      

      MyAccessDeniedHandler:

      public class MyAccessDeniedHandler implements AccessDeniedHandler {
          /* ... */
          @Override
          public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception)
                  throws IOException, ServletException {
              if (exception instanceof MissingCsrfTokenException) {
                  /* Handle as a session timeout (redirect, etc).
                  Even better if you inject the InvalidSessionStrategy
                  used by your SessionManagementFilter, like this:
                  invalidSessionStrategy.onInvalidSessionDetected(request, response);
                  */
              } else {
                  /* Redirect to a error page, send HTTP 403, etc. */
              }
          }
      }
      

      或者,您可以将自定义处理程序定义为DelegatingAccessDeniedHandler

      <bean id="myAccessDeniedHandler" class="org.springframework.security.web.access.DelegatingAccessDeniedHandler">
          <constructor-arg name="handlers">
              <map>
                  <entry key="org.springframework.security.web.csrf.MissingCsrfTokenException">
                      <bean class="org.springframework.security.web.session.InvalidSessionAccessDeniedHandler">
                          <constructor-arg name="invalidSessionStrategy" ref="myInvalidSessionStrategy" />
                      </bean>
                  </entry>
              </map>
          </constructor-arg>
          <constructor-arg name="defaultHandler">
              <bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
                  <property name="errorPage" value="/my_error_page"/>
              </bean>
          </constructor-arg>
      </bean>
      

      【讨论】:

        猜你喜欢
        • 2018-09-01
        • 2018-03-20
        • 2017-10-01
        • 2015-11-13
        • 2020-12-19
        • 2014-09-25
        • 2018-01-04
        • 2015-09-15
        • 2013-01-12
        相关资源
        最近更新 更多