3

我对 Java/Spring/MVC 比较陌生。我希望有人能对此有所启发,因为这个问题将在没有得到解决的情况下毁掉另一个周末。

目前我正在做一个项目,实施几个 RFC。每个 RFC 都有自己的“挑战”,从更改休眠查询和存储过程到微小的文本更改以及向表单添加额外字段。

我现在面临的问题(我认为)与 Spring 安全性有关。

目前的情况:
要登录到应用程序,用户由登录页面(/user/login.jsp)呈现。输入用户名和密码后,用户被发送到欢迎页面(/welcome/hello.jsp)。

如果用户忘记了他们的密码,他们会使用一个链接发送一封电子邮件,其中提供一个链接到可以输入新密码的页面 (/welcome/newPassword?code=xxx)。输入提交按钮将进行一些验证,将密码保存在数据库中并在页面上显示“密码已更改”消息。要进入应用程序,用户现在必须使用新密码登录。

客户想要什么:提交新密码后,用户应该会自动使用新密码登录并重定向到/welcome/hello.jsp

我在网上搜索,但找不到我正在寻找的场景(或者可能没有认出它!)。

我认为可以解决问题的方法:

我在 WelcomeController 中有这个方法,它目前处理新密码:

@RequestMapping(method = RequestMethod.POST)
public void newPassword(Model model, HttpSession httpSession, ModelAttribute(PASSWORD) Passwords password, BindingResult result) {
    logger.debug("Posting password for new password");
    ScreenMessage message = null;

    //
    try {
        // errors from initBinder - back to page
        if (result.hasErrors()) {
            return;
        }

        // Validate input: on error go back to form
        passwordValidator.validate(password, result);
        if (result.hasErrors()) {
            return;
        }

        // Save password
        passwordService.createNewOwnPassword(result, password);
        if (result.hasErrors()) {
            return;
        }

        // No errors
        message = new ScreenMessage();
        message.setText(CHANGED);
        message.setArg(THIS_PASSWORD, 0);
        model.addAttribute(MESSAGE, message);
        httpSession.setAttribute(ACTION_READ, Boolean.TRUE);

        // Errors are fatal.
    } catch (Exception e) {
        logger.error("ERROR-CHANGE-password for user: " + password.getUser().getLoginName(), e);
        result.reject(  ERROR_FATAL, new Object[] { password}, ERROR_FATAL);
    } finally {
        model.addAttribute(PASSWORD, password);
    }
}

我想我可以简单地改变这个方法让它返回一个 ModelAndView 并使用重定向:

ModelAndView mav = new ModelAndView("redirect:/welcome/hello", (ModelMap) model);
        return mav; 

但是:它一直将我发送到 /user/login,正是我不想去的页面......

可能很方便的额外信息:

据我所知,该应用程序使用的是 Spring 2.5。

(我希望相关)我的 security-applicationContext.xml 的一部分:

    <!--  always-use-default-target="true" -->
    <security:form-login login-page="/dyn/user/login" 
        login-processing-url="/dyn/user/j_spring_security_check"
        default-target-url="/dyn/welcome/hello" 
        always-use-default-target="true"
        authentication-failure-handler-ref="exceptionTranslationRouting" />

    <security:access-denied-handler error-page="/dyn/welcome/hello" />

    <security:logout logout-success-url="/dyn/user/logout" invalidate-session="true" />

我重定向到 /welcome/hello 的想法是不是太简单了,还是我只是错过了一些小东西?

4

1 回答 1

1

Firstly, you'd better move your logic from web controller (I don't like password validation) to component dedicated to password change. This will allow you to logically separate parts of your application. Filter maybe? (more later) Or special service?

Secondly, take a look at the source code of Spring Security. From my experience, I can tell you that any customization in Spring Security is only possible if you understand its architecture (especially filter chain). I would recommend taking look at class AbstractAuthenticationProcessingFilter. It's responsible for authenticating users when they provide username and password in your application. Take a look at the method AbstractAuthenticationProcessingFilter.successfulAuthentication.

This line of that method sets security context which makes the user logged in:

 SecurityContextHolder.getContext().setAuthentication(authResult);

Reference authResult is of type Authentication. It is an interface implemented by for instance UsernamePasswordAuthenticationToken. Take a look at UsernamePasswordAuthenticationFilter.attemptAuthentication method for example of how to construct such an object.

Going back to filter solution: I personally would think about implementing my own filter that would extend AbstractAuthenticationProcessingFilter. It would be responsible for password change. That way you don't have to think about things like session change on successful authentication. You just need to inject it in you XML.

To sum up,

classes that are recommended to look at:

  • AbstractAuthenticationProcessingFilter
  • UsernamePasswordAuthenticationToken
  • UsernamePasswordAuthenticationFilter

You should read Spring Security Documentation, especially parts about architecture, filter chain and its role in authenticating users.

http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#overall-architecture

于 2012-11-03T21:52:27.157 回答