如何在使用Spring Security时从登录失败中获取用户名?

29

我们正在使用spring security 3.0.5,Java 1.6和Tomcat 6.0.32版本。在我们的.xml配置文件中,我们有:

<form-login login-page="/index.html" default-target-url="/postSignin.html" always-use-default-target="true"
 authentication-failure-handler-ref="authenticationFailureHandler"/>

我们的authenticationFailureHandler定义如下:

<beans:bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
   <beans:property name="exceptionMappings">
      <beans:props>
    <beans:prop key="org.springframework.security.authentication.BadCredentialsException">/index.html?authenticationFailure=true</beans:prop>
    </beans:props>
   </beans:property>
</beans:bean>

Java

-->

Java

    @RequestMapping(params={"authenticationFailure=true"}, value ="/index.html")
    public String handleInvalidLogin(HttpServletRequest request) {
       //...  How can I get the username that was used???
       // I've tried:
       Object username = request.getAttribute("SPRING_SECURITY_LAST_USERNAME_KEY");
       Object username = request.getAttribute("SPRING_SECURITY_LAST_USERNAME");  // deprecated
    }

因此,我们将所有的BadCredentialsExceptions重定向到index.htmlIndexController。在IndexController中,我希望能够获取用于登录失败尝试的username。我该如何做到这一点?

7个回答

30

好的,答案证明了一件极其简单的事情,但据我所知,这并没有被广泛讨论或记录。

这就是我所要做的(没有任何配置,只需创建这个类)...

import org.apache.log4j.Logger;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationListener implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> {
    private static final Logger LOG = Logger.getLogger(MyApplicationListener.class);

    @Override
    public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent event) {
        Object userName = event.getAuthentication().getPrincipal();
        Object credentials = event.getAuthentication().getCredentials();
        LOG.debug("Failed login using USERNAME [" + userName + "]");
        LOG.debug("Failed login using PASSWORD [" + credentials + "]");
    }
}

我离 Spring Security 专家还有很大的差距,如果有任何人阅读此内容并知道我们不应该这样做或者知道更好的方法,请告诉我。


我们在我们的设置中已经有这个完整的代码运行。我无法告诉您为什么它在您的设置中不起作用。我们没有配置任何新内容,也没有更改任何 .xml 文件。注释似乎将Spring指向此代码,并在登录失败时执行它。对我们来说,它完美地工作。 - kasdega
也许需要一些其他的配置。顺便说一下,我已经找到了另一种解决方案。不过还是谢谢你提供的信息 :) - Hoàng Long
嗨Kasdega, 我如何在控制器中使用上述解决方案来访问用户名?我想在控制器中获取用户名。 - LittleLebowski
1
@LittleLebowski 我相信Hoang Long已经通过他对我的问题的回答回答了你的问题。String username = (String) request.getSession().getAttribute("SPRING_SECURITY_LAST_USERNAME"); - kasdega
@kasdega,你需要在“ApplicationContext.xml”或其他可见于项目应用上下文的配置文件中创建一个“MyApplicationListner”类的bean。 - hirantha129
显示剩余2条评论

22
我是这样做的:
  1. 创建了一个继承SimpleUrlAuthenticationFailureHandler类的类。

  2. 重写了onAuthenticationFailure方法,并以HttpServletRequest作为参数接收。

  3. request.getParameter("username"),其中"username"是我HTML表单中输入框的名称。


1
它运行良好,只是一个注释,如果您在表单中使用输入文本的默认名称j_username,那么您必须像这样捕获它:request.getParameter("j_username") - OJVM

5

1
你需要连接你自己的实现。看看是否可以扩展DefaultAuthenticationEventPublisher。然后将其连接到ProviderManager(它是AuthenticationManager的一个实现,在Spring Security中需要声明)。那个类有一个方法叫做setAuthenticationEventPublisher。 - Saish

4
这个解决方案并没有太多信息,但如果在Spring Security配置中设置failureForwardUrl,您将被转发到错误页面而不是重定向。然后您可以轻松获取用户名和密码。 例如,在您的配置中添加: .and().formLogin().failureForwardUrl("/login/failed") (* url必须与登录页面url不同)
在您的登录控制器中,需要添加以下内容:
@RequestMapping(value = "/login/failed")
public String loginfailed(@ModelAttribute(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY) String user, @ModelAttribute(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY) String password) {
     // Your code here
}

SPRING_SECURITY_FORM_USERNAME_KEY,SPRING_SECURITY_FORM_PASSWORD_KEY 是默认名称,但您也可以在 FormLoginConfigurer 中进行设置:

.and().formLogin().usernameParameter("email").passwordParameter("password").failureForwardUrl("/login/failed")

1

我发现这对我有用。不幸的是,我仍然没有在SpringSecurity文档中找到确切的位置:

在任何操作中,您都可以检查上次使用的用户名(无论登录是否失败):

    String  username = (String) request.getSession().getAttribute("SPRING_SECURITY_LAST_USERNAME");

1
UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY 是对应于属性名称的常量。它现在已经过时。 - mrbaboo
@mrbaboo:所以这就是为什么我找不到关于这个常量的任何信息。所以它已经被弃用了,你知道实现相同效果的其他方法吗? - Hoàng Long
3
我想指出,在Spring Security的3.1.0版本中,存储此弃用值的代码已被删除。如果您想在最新版本中实现此效果,您将需要使用上述其他方法之一。 - Jules

1
如果你想使用Spring AOP,你可以将以下代码添加到你的Aspect类中:
private String usernameParameter = "username"; 

@Before("execution(* org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler.onAuthenticationFailure(..))")
public void beforeLoginFailure(JoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = (HttpServletRequest) joinPoint.getArgs()[0];
        AuthenticationException exceptionObj = (AuthenticationException) joinPoint.getArgs()[2];

        String username = request.getParameter(usernameParameter);

        System.out.println(">>> Aspect check: AuthenticationException:  "+exceptionObj.getMessage());
        System.out.println(">>> Aspect check: user: "+ username + " failed to log in.");
}

0

这是一个相当古老的帖子,但如果您正在使用相对较新的“spring-boot-starter-security”包,那么我是这样做的:

我像这样设置了我的AuthenticationFailureHandler:

SimpleUrlAuthenticationFailureHandler handler = new SimpleUrlAuthenticationFailureHandler("/my-error-url");
handler.setUseForward(true);

这将把最后一个异常设置到请求中:

//from SimpleUrlAuthenticationFailureHandler source
request.setAttribute("SPRING_SECURITY_LAST_EXCEPTION", exception);

然后,从我的控制器中,我可以获取错误的用户名:

RequestMapping("/impersonate-error")
public String impersonateErrorPage(Map<String, Object> model, HttpServletRequest request) {

    AuthenticationException ex = (AuthenticationException)request.getAttribute("SPRING_SECURITY_LAST_EXCEPTION");
    if(ex != null) {
        logger.debug("Impersonate message: " + ex.getMessage());
        model.put("badName", ex.getMessage());
    }
    return "impersonate-error";
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接