记住我和身份验证成功处理程序

时间:2012-07-20 08:42:02

标签: spring spring-security

我有登录成功和重定向到页面的奇怪问题。

下面是我的弹簧安全配置。

<http auto-config="true" use-expressions="true">
    <intercept-url pattern="/login.hst**" access="anonymous or authenticated" />
    <intercept-url pattern="/**/*.hst" access="authenticated" />
    <form-login login-page="/login.hst"
        authentication-failure-url="/login.hst?error=true"
        authentication-success-handler-ref="loginSucessHandler" />
    <logout invalidate-session="true" logout-success-url="/home.hst"
        logout-url="/logout.hst" />
    <remember-me key="jbcpHaverERP" authentication-success-handler-ref="loginSucessHandler"/>
    <session-management>
    <concurrency-control max-sessions="1" />
</session-management>
</http>

LoginSuessHandler类:

@Service
public class LoginSucessHandler extends
        SavedRequestAwareAuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
            HttpServletResponse response, Authentication authentication)
            throws ServletException, IOException {
            ...
        super.setUseReferer(true);
        super.onAuthenticationSuccess(request, response, authentication);
    }

}

现在成功重定向到请求页面的问题。如果我直接引用任何安全网址spring将我重定向到登录页面并成功登录到原始请求的链接。 但是,如果用户之前选择了remember-me然后关闭浏览器并且现在请求直接URL,那么这不起作用,他正在进行适当的身份验证,而不是将他重定向到请求的页面重定向到/。我检查了日志和一些弹簧源代码,发现它无法确定目标网址。

我试图设置引用但引用值为null。但有一件奇怪的事我注意到,在spring安全配置中,如果我从remember-me配置中删除authentication-success-handler,那么它可以工作。

    <remember-me key="jbcpHaverERP" authentication-success-handler-ref="loginSucessHandler"/>

无法找出问题。是身份验证成功处理程序实现需要不同的表单登录和记住我吗?

4 个答案:

答案 0 :(得分:14)

记住我与表单登录的不同之处在于,在用户提出的实际请求期间进行身份验证。对于表单登录,必须首先将用户重定向到登录页面,提交登录表单,然后将其重定向到原始目标(通常在会话中缓存)。因此,表单登录需要重定向,而记住我不需要。通过记住我的请求,可以对用户进行身份验证,并允许请求继续进行而无需任何干预。

AuthenticationSuccessHandler的主要目的是在身份验证后控制导航流程,因此您通常不会使用记住我的导航流程。使用SavedRequestAwareAuthenticationSuccessHandler不是一个好主意,因为没有可用的已保存请求。如果没有已保存的请求,则默认情况下它将执行重定向到您观察到的“/”。

如果你想要的是在记住我的登录过程中添加一些功能,那么你可以直接实现AuthenticationSuccessHandler接口,而无需执行重定向或转发。正如我上面所解释的,您不能对表单登录使用相同的实现,因为当前请求是提交登录表单(通常是URL j_spring_security_check),而不是对您的URL的请求应用。因此,您需要重定向表单登录。

答案 1 :(得分:13)

您宁愿使用ApplicationListener并查找事件InteractiveAuthenticationSuccessEvent

InteractiveAuthenticationSuccessEvent有一个属性generatedBy,它将是过滤器,即UsernamePasswordAuthenticationFilter(表单登录)和RememberMeAuthenticationFilter(记住登录)

@Component
class AuthenticationApplicationListener implements ApplicationListener<InteractiveAuthenticationSuccessEvent> {

  @Override
  void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
    //do something
  }

}
在rememberMe上使用AuthenticationSuccessHandler的自定义实现会导致问题。看看RememberMeAuthenticationFilter中的流程。如果使用successHandler,则绕过过滤器链

答案 2 :(得分:2)

使用AuthenticationSuccessHandler不起作用。如另一个答案所述,弹簧安全过滤器链将被绕过! 什么有效,是使用ApplicationListener - 另一个答案也提出。但要了解一下,如果您的用户通过记住我进行身份验证,那么使用InteractiveAuthenticationSuccessEvent.getGeneratedBy()的想法就无效了:getGeneratedBy会返回Class<T>,这意味着通用。因此,在运行时,如果TRememberMeAuthenticationFilter,则无法找到。

什么对我有用:使用InteractiveAuthenticationSuccessEvent.getAuthentication()

这里有一个例子(顺便说一句:@EventListener从Spring Security 4.2开始使用 - 如果您使用的是早期版本,请通过实现ApplicationListener<InteractiveAuthenticationSuccessEvent>来执行以下操作:

@Component
public class AuthenticationApplicationListener {

    @EventListener
    public void handleInteractiveAuthenticationSuccess(InteractiveAuthenticationSuccessEvent event) {
        if (RememberMeAuthenticationToken.class.isAssignableFrom(event.getAuthentication().getClass())) {
            .... do some stuff
    }
  }
 }

答案 3 :(得分:1)

您应该为登录表单和记住我实现不同的身份验证成功处理程序。 如果要在remeber-me处理程序中执行重定向,可以使用SimpleUrlAuthenticationSuccessHandler并设置DefaultTargetUrl。

public class RememberMeAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
    Authentication authentication) throws IOException, ServletException {

    // ...

    super.setAlwaysUseDefaultTargetUrl(true);
    super.setDefaultTargetUrl(request.getRequestURL().toString());
    super.onAuthenticationSuccess(request, response, authentication);
}
相关问题