简体   繁体   中英

Spring Security authenticationSuccessHandler for RememberMeAuthenticationFilter

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

Here is my spring security configuration

<?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 AuthenticationSuccessHandler implementation

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 onAuthenticationSuccess isn't called at all. I've read an answer on StackOverflow that says that I need to implement onAuthenticationSuccess instead 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'

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

So, first, add the following logic to your 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:http section as:

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

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

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

because this is missing from your form authentication.

Additionally, based on Spring Security documentation on filter positions, it is advised that you do not use auto-config with custom filters.

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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