登录尝试失败后,在表单输入字段中保留用户名(Java / Spring-security)

时间:2015-07-27 18:07:10

标签: login spring-security thymeleaf

我已成功使用Spring-Boot和基于表单的身份验证设置Spring-Security MVC应用程序。我使用thymeleaf实现了一个自定义登录表单,基本上看起来像:

<form th:action="@{/login}" method="POST">
  <label for="username">Username</label>
  <input type="text" id="username" name="username"/>
  <label for="password">Password</label>
  <input type="password" id="password" name="password"/>
  <button type="submit">Log in</button>
</form>

表单在扩展addViewControllers的类的WebMvcConfigurerAdapter方法中注册为简单的“视图控制器”,如:

public class LoginController extends WebMvcConfigurerAdapter {
  @Override
  public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/login").setViewName("login/login");
    registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
  }
}

到目前为止这是有效的。有一件事困扰我:在登录尝试失败后,应用程序返回到登录表单(这很好),但两个字段都被清除。我想在输入字段中保留用户名(如果密码拼写错误的话)。

我试过

<input type="text" id="username" name="username" th:value="username"/>

但这没有帮助。

有没有办法从百万富翁模板中的失败尝试中获取用户名?我需要为此编写一个显式控制器吗?

2 个答案:

答案 0 :(得分:1)

此问题的关键在于SimpleUrlAuthenticationFailureHandler(默认AuthenticationFailureHandler)执行重定向 - 因此请求参数(包括用户名)将无法用于登录表单。

奇怪的是,对于这样一个显而易见的事情,似乎并不是一个优雅的解决方案。

如果您只需要保留用户名,那么快速(尽管不是很满意)的选项是在模板中使用SPRING_SECURITY_LAST_EXCEPTION.authentication.principal(但请注意,首次访问该页面时它将为空)。 / p>

然而,您会发现“记得我”的相同问题。复选框,如果你使用它。

我最终扩展了SimpleUrlAuthenticationFailureHandler并在重定向之前将我想要保留的请求参数(usernameremember-me)添加到网址。

我很想知道是否有更好的方法来做到这一点。

答案 1 :(得分:0)

正如马丁·威尔逊已经指出的那样,这个问题没有优雅的解决方案。但是,我在Spring MVC应用程序中解决了这个问题而没有使用额外的URL参数。希望这也有助于你。

我将ExceptionMappingAuthenticationFailureHandler扩展为在会话参数(USERNAME_PARAMETER)中存储登录表单的用户名参数(LAST_USERNAME_KEY):

public class CustomAuthenticationFailureHandler extends ExceptionMappingAuthenticationFailureHandler {

    // Username field name in the login form
    private final static String USERNAME_PARAMETER = "email";
    // Parameter for retained username
    private static final String LAST_USERNAME_KEY = "LAST_USERNAME";

    public CustomAuthenticationFailureHandler(String defaultFailureUrl) {
        super.setDefaultFailureUrl(defaultFailureUrl);
    }

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {

        super.onAuthenticationFailure(request, response, exception);
        String lastUserName = request.getParameter(USERNAME_PARAMETER);

        // Store the given username in the session
        try {
            HttpSession session = request.getSession(false);
            if (session != null || isAllowSessionCreation()) {
                request.getSession().setAttribute(LAST_USERNAME_KEY, lastUserName);
            }
        } catch (IllegalStateException illegalStateException) {
            LOGGER.debug(Arrays.toString(illegalStateException.getStackTrace()));
        }
    }
}

这必须注册为bean:

@Bean
public LogoutSuccessHandler CustomLogoutSuccessHandler() {
    // To redirect to previous url.
    return new CustomLogoutSuccessHandler("/access");
}

它在我的登录表单中如下工作。现在,会话参数LAST_USERNAME包含最后一个用户名:

<input class="form-control" name="email" id="email" value="${LAST_USERNAME}" required>