简体   繁体   English

Spring CSRF覆盖安全XML配置中的“POST”注销行为

[英]Spring CSRF override “POST” logout behaviour in security XML config

Currently we have a problem with Spring CSRF solution for our legacy App because CSRF implementation changes behavior of default Spring security Spring security configuration sis following: 目前我们的遗留应用程序的Spring CSRF解决方案存在问题,因为CSRF实现更改了默认Spring安全性Spring安全配置的行为:

<http pattern="">
...
<logout
                logout-url="/logout"
                delete-cookies="..."
                success-handler-ref="logoutSuccessHandler"
                />
<csrf/>
</http>

org.springframework.security.config.annotation.web.configurers.LogoutConfigurer Logout configurer. org.springframework.security.config.annotation.web.configurers.LogoutConfigurer注销配置器。 According to Spring documentation: 根据Spring文档:

Adding CSRF will update the LogoutFilter to only use HTTP POST. 添加CSRF会将LogoutFilter更新为仅使用HTTP POST。 This ensures that log out requires a CSRF token and that a malicious user cannot forcibly log out your users. 这可确保注销需要CSRF令牌,并且恶意用户无法强制注销您的用户。

Code that makes this change is the following: 进行此更改的代码如下:

 private RequestMatcher getLogoutRequestMatcher(H http) {
        if(logoutRequestMatcher != null) {
            return logoutRequestMatcher;
        }
        if(http.getConfigurer(CsrfConfigurer.class) != null) {
            this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl, "POST");
        } else {
            this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl);
        }
        return this.logoutRequestMatcher;
    }

Generally for CSRF protection such behavior makes perfect sense. 通常,对于CSRF保护,这种行为非常有意义。 But as for me it's very strange that this implementation is not flexible (why hardcode real implementations and not autowire dependencies?). 但对于我来说,这种实现不灵活(为什么硬编码实际的实现而不是autowire依赖?)是非常奇怪的。

The problem is that our application is built in such way that before regular Spring logout it's performed additional clean up in Spring Controllers. 问题是我们的应用程序是这样构建的,即在常规Spring注销之前,它会在Spring Controllers中执行额外的清理。 Mainly it was implemented Switch User feature but in a custom way. 主要是它实现了Switch User功能,但是以自定义方式实现。 So, changing logout link to perform POST is not an option because mainly clean up is performed on custom Controller. 因此,更改注销链接以执行POST不是一种选择,因为主要是在自定义控制器上执行清理。

It seems in order to use specified approach there is only one possible solution: 似乎为了使用指定的方法,只有一种可能的解决方案:

@RequestMapping(value = "/logout", method = RequestMethod.GET) //or it can be a post
    public String logout() {
// 1. Perform Clean up
// 2. Decide whether to logout or redirect to other page
// 3. Perform redirect based on decision
}

//If it's decided to logout this will go to this Controller method: //如果决定退出,则会转到此Controller方法:

  @RequestMapping(value = "csrflogout", method = RequestMethod.GET)
    public void csrfLogout(){
//1 Create manual post request
//2. Copy session information
//3. Perform Post to logout URL that is specified in security xml
     }

Generally this approach is not good from code quality perspective. 从代码质量的角度来看,这种方法通常并不好。 So, there are two questions: 所以,有两个问题:

  1. What is the reason to make such strict implementation in Spring and don't provide any visible possibility to override it (specifically I provided code example how it's created )? 在Spring中进行如此严格的实现的原因是什么,并且没有提供覆盖它的任何可见的可能性(特别是我提供了代码示例如何创建它)?
  2. Any good alternative to fix mentioned problem. 修复上述问题的任何好方法。

The behavior you describe is the behavior if you don't explicitly configure logout support but only enable it, it you explicitly configure it it will use that configuration instead. 您描述的行为是行为,如果您没有显式配置注销支持但只启用它,您明确配置它将使用该配置。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}

This is also documented in the reference guide. 这也被记录在参考指南中。

However the real solution is imho that you shouldn't use a controller for the additional logout functionality but use a LogoutHandler instead. 然而,真正的解决方案是你不应该使用控制器来实现额外的注销功能,而是使用LogoutHandler That will integrate nicely with Spring Security and you don't need to redirect/forward to different URLs. 这将很好地与Spring Security集成,您不需要重定向/转发到不同的URL。

Basically the main complexity was in the overriding logoutFilter in the Spring Security XML context to work with default implementation of org.springframework.security.web.util.matcher.AntPathRequestMatcher (that works with "GET" not "POST" requests). 基本上,主要的复杂性是在Spring Security XML上下文中重写logoutFilter,以使用org.springframework.security.web.util.matcher.AntPathRequestMatcher的默认实现(使用“GET”而不是“POST”请求)。 In order to do this several beans were added to security xml context: 为了做到这一点,将几个bean添加到安全xml上下文中:

 <bean id="logoutAntPathRequestMatcher" class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
        <constructor-arg value="logout" />
    </bean>

and logout filter itself: 和注销过滤器本身:

<bean id="logoutFilter"
    class="org.springframework.security.web.authentication.logout.LogoutFilter">
    <constructor-arg  name="logoutSuccessHandler" ref="logoutSuccessHandler"/>
    <constructor-arg  name="handlers">
        <list>
            <ref bean="securityContextLogoutHandler" />
            <ref bean="cookieClearingLogoutHandler" />
            <ref bean="csrfLogoutHandler" />
        </list>
    </constructor-arg>
    <property name="filterProcessesUrl" value="/logout"/>
    <property name="logoutRequestMatcher" ref="logoutAntPathRequestMatcher"/>
</bean>

I saw the same error after Internet Explorer 11 update. Internet Explorer 11更新后,我看到了同样的错误。 CsrfConfigurer.class is not null and post is expected when logging out. CsrfConfigurer.class不为null,并且在注销时需要发布。

if(http.getConfigurer(CsrfConfigurer.class) != null) {
            this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl, "POST");
        } else {
            this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl);
        }

I solved my problem by bypassing the logoutfilter and insert new filter to spring security 我通过绕过logoutfilter并插入新的过滤器来解决我的问题

Example is below. 示例如下。

    <beans:bean id="logoutAntPathRequestMatcher" class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
            <beans:constructor-arg value="/logout"/>
        </beans:bean>

        <beans:bean id="securityContextLogoutHandler" class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">

        </beans:bean>


        <beans:bean id="cookieClearingLogoutHandler" class="org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler">
            <beans:constructor-arg value="JSESSIONID"/>
        </beans:bean>

        <beans:bean id="logoutFilter"
              class="org.springframework.security.web.authentication.logout.LogoutFilter">
            <beans:constructor-arg  name="logoutSuccessUrl" value="/login"/>
            <beans:constructor-arg  name="handlers">
                <beans:list>
                    <beans:ref bean="securityContextLogoutHandler" />
                    <beans:ref bean="cookieClearingLogoutHandler" />
                </beans:list>
            </beans:constructor-arg>
            <beans:property name="filterProcessesUrl" value="/logout"/>
            <beans:property name="logoutRequestMatcher" ref="logoutAntPathRequestMatcher"/>
        </beans:bean>


<http>
...
<sec:custom-filter ref="logoutFilter" after="LOGOUT_FILTER"/>
...
</http>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM