【问题标题】:Spring Security authenticationSuccessHandler for RememberMeAuthenticationFilter用于 RememberMeAuthenticationFilter 的 Spring Security authenticationSuccessHandler
【发布时间】:2013-09-18 05:23:18
【问题描述】:

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

这是我的 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>

这是我的自定义 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;
    }
}

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

【问题讨论】:

    标签: java spring spring-mvc spring-security


    【解决方案1】:

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

    所以,首先,将以下逻辑添加到您的CustomSavedRequestAwareAuthenticationSuccessHandler

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

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

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

    第三,将security:http部分中的customer filer定义为:

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

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

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

    因为您的表单身份验证中缺少此内容。

    此外,根据关于过滤器位置的Spring Security 文档,建议您不要auto-config 与自定义过滤器一起使用。

    注意:

    1. 您看到这种行为的原因是当您看到登录表单时,它与记住我的服务无关。表单处理决定了最终的目标 URL。
    2. 第一次之后,将由 remember-me 过滤器进行身份验证,并再次需要发送 HTTP 响应代码。

    我还建议阅读this answer,因为它可以更深入地了解 Spring Security 中 form-login、http-basic auth 和 remember-me 服务之间的区别。

    【讨论】:

    • 感谢您的回答。现在它不再重定向页面了。 'response.setStatus(HttpServletResponse.SC_OK);'每次登录时都会执行,但之后没有响应。虽然从不调用 HttpResponseAuthenticationFilter 上的 onSuccessfulAuthentication 方法。你知道问题出在哪里吗?
    • 尝试使用this answer,看看它是如何发出HTTP响应的。基本上,为此尝试类似 cURL 的方法并观察 HTTP 响应标头和内容。
    • 它返回 200 这是我想要的。但是当我将其更改为其他代码时,它仍然返回 200。这是怎么回事?
    • 我的猜测是,HttpAuthenticationFilter 可能应该扩展 UsernamePasswordAuthenticationFilter 而不是 RememberMeAuthenticationFilter,因为一旦在请求中找到身份验证,第二个就会更改响应代码。另请注意,successfulAuthentication 有两种风格,其中一种允许您修改 FilterChain
    • 好的,我将删除将 HttpServletResponse.SC_OK 放入响应的条件。因为它什么都不做。再次感谢您的回答。
    猜你喜欢
    • 2016-07-17
    • 1970-01-01
    • 2014-01-04
    • 2019-11-02
    • 1970-01-01
    • 2011-11-20
    • 2011-09-19
    • 2017-05-12
    • 2014-05-05
    相关资源
    最近更新 更多