Java 用于 RememberMeAuthenticationFilter 的 Spring Security authenticationSuccessHandler

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18864459/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 11:44:33  来源:igfitidea点击:

Spring Security authenticationSuccessHandler for RememberMeAuthenticationFilter

javaspringspring-mvcspring-security

提问by William Wino

I want to implement a custom AuthenticationSuccessHandlerfor my login filter which is org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.

我想AuthenticationSuccessHandler为我的登录过滤器实现一个自定义org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter

Here is my spring security configuration

这是我的 spring 安全配置

<?xml version="1.0" encoding="UTF-8"?>
<beans 
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:security="http://www.springframework.org/schema/security"
    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:http entry-point-ref="restAuthenticationEntryPoint" disable-url-rewriting = "true" auto-config="true" use-expressions="true">
        <security:intercept-url pattern="/api/*" access="hasRole('AUTHENTICATED_USER')"/>
        <security:remember-me key="spring_login_detail" services-ref="rememberMeServices"/>
        <security:form-login login-processing-url="/login"/>

        <security:logout
            invalidate-session="true"
            delete-cookies="JSESSIONID,SPRING_SECURITY_REMEMBER_ME_COOKIE"
            logout-url="/logout" 
        />
    </security:http>

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

    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider ref="rememberMeAuthenticationProvider"/>
        <security:authentication-provider user-service-ref="customUserDetailsService">
            <security:password-encoder ref="passwordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

    <bean class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" id="passwordEncoder"/>

    <bean id="mySuccessHandler" class="com.projectname.security.CustomSavedRequestAwareAuthenticationSuccessHandler"/>

    <bean id="customUserDetailsService" class="com.projectname.security.CustomUserDetailsService"/>

    <bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
        <property name="key" value="jsfspring-sec" />
        <property name="userDetailsService" ref="customUserDetailsService" />
        <property name="alwaysRemember" value="false" />
        <property name="tokenValiditySeconds" value="1209600" />
        <property name="parameter" value="_spring_security_remember_me_input"/>
    </bean>

    <bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
        <property name="key" value="spring_login_detail"/>
    </bean>

    <bean id="rememberMeFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
        <property name="rememberMeServices" ref="rememberMeServices"/>
        <property name="authenticationManager" ref="authenticationManager" />
        <property name="authenticationSuccessHandler" ref="mySuccessHandler"/>
    </bean> 

</beans>

Here's my custom AuthenticationSuccessHandlerimplementation

这是我的自定义AuthenticationSuccessHandler实现

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.util.StringUtils;

public class CustomSavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    private RequestCache requestCache = new HttpSessionRequestCache();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws ServletException, IOException {
        SavedRequest savedRequest = requestCache.getRequest(request, response);
        if (savedRequest == null) {
            clearAuthenticationAttributes(request);
            return;
        }

        String targetUrlParam = getTargetUrlParameter();
        if (isAlwaysUseDefaultTargetUrl()
                || (targetUrlParam != null
                && StringUtils.hasText(request.getParameter(targetUrlParam)))) {
            requestCache.removeRequest(request, response);
            clearAuthenticationAttributes(request);
            return;
        }

        clearAuthenticationAttributes(request);
    }

    public void setRequestCache(RequestCache requestCache) {
        this.requestCache = requestCache;
    }
}

The problem is after a successful authentication the onAuthenticationSuccessisn't called at all. I've read an answer on StackOverflow that says that I need to implement onAuthenticationSuccessinstead of SimpleUrlAuthenticationSuccessHandler. I've tried to do that, still didn't work. Everything else is fine, the only problem is each time I log in, spring just redirected me to '/'. Which is not what I want, I want it just to return '200 OK'

问题是在成功验证之后onAuthenticationSuccess根本没有调用。我在 StackOverflow 上阅读了一个答案,说我需要实现onAuthenticationSuccess而不是SimpleUrlAuthenticationSuccessHandler. 我试过这样做,仍然没有奏效。其他一切都很好,唯一的问题是每次登录时,spring 只是将我重定向到'/'. 这不是我想要的,我想要它返回'200 OK'

采纳答案by nobeh

Assuming that I've understood correctly, you want to break the filter chain and send an HTTP response codewhen an authentication succeeds no matter how the authentication happens; i.e. it can be login form or remember-me authentication.

假设我理解正确,无论身份验证如何发生,您都希望在身份验证成功时中断过滤器链并发送 HTTP 响应代码;即它可以是登录表单或记住我的身份验证。

So, first, add the following logic to your CustomSavedRequestAwareAuthenticationSuccessHandler:

因此,首先,将以下逻辑添加到您的CustomSavedRequestAwareAuthenticationSuccessHandler:

// place where applicable
if (authentication != null) {
  response.setStatus(HttpServletResponse.SC_OK);
}

Second, define a new filter such as :

其次,定义一个新的过滤器,例如:

class HttpResponseAuthenticationFilter extends RememberMeAuthenticationFilter {

  protected  void   onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
    super.onSuccessfulAuthentication(request, response, authResult);
    if (authResult != null) {
      response.setStatus(HttpServletResponse.SC_OK);
    }
  }

} 

Third, define the customer filer in security:httpsection as:

第三,在security:http节中定义客户归档器为:

<custom-filter position="LAST" ref="myHttpResponseAuthFilter" />

Fourth, add a reference for your success handler to your form-loginas:

第四,将您的成功处理程序的引用添加到您的form-loginas:

<form-login ... authentication-success-handler-ref="mySuccessHandler" ... />

because this is missing from your form authentication.

因为您的表单身份验证中缺少这一点。

Additionally, based on Spring Securitydocumentation on filter positions, it is advised that you do notuse auto-configwith custom filters.

此外,基于Spring Security的过滤器上的位置的文档,建议您不要使用auto-config自定义过滤器。

Observe that:

请注意:

  1. The reason you see this behavior is that when you see the login form, it's not about the remember-me services. The form processing decided the final target URL.
  2. After the first time, it'd be the remember-me filter that authenticates and again needs to send an HTTP response code.
  1. 您看到此行为的原因是当您看到登录表单时,它与“记住我”服务无关。表单处理决定了最终的目标 URL。
  2. 在第一次之后,它会是记住我过滤器进行身份验证并再次需要发送 HTTP 响应代码。

I also suggest reading this answeras it gives more insight into the difference between form-login, http-basic auth, and remember-me services in Spring Security.

我还建议阅读此答案,因为它可以更深入地了解 Spring Security 中的表单登录、http-basic 身份验证和记住我的服务之间的区别。