为Spring 4 + Spring Security 3.2.5实现失败处理程序表单(无XML)

时间:2015-02-18 17:32:32

标签: spring spring-security

尝试登录应用程序并收到CredentialsExpiredException后,如何呈现更改密码表单?如何支持其他参数如新密码和确认?

1 个答案:

答案 0 :(得分:0)

  1. 将WebSecurityConfigurerAdapter实现类配置为:

    http.
    //...
    .antMatchers("/resources/**", "/login", "/index.**").permitAll()
    //...
    .authenticationDetailsSource(req -> new CustomWebAuthenticationDetails(req))
    .failureHandler((req, res, e) -> {
        req.getSession().setAttribute("SPRING_SECURITY_LAST_EXCEPTION", e);
        res.sendRedirect((e instanceof CredentialsExpiredException) ? CHANGE : FAILURE);
    })
    //...
    
  2. 其中:

        private static final String FAILURE = "/index.html?login_error=true";
        private static final String CHANGE = FAILURE + "&change=true";
    

        public class CustomWebAuthenticationDetails extends WebAuthenticationDetails {
    
        private Map<String, String> params = new HashMap<>();
    
        public CustomWebAuthenticationDetails(HttpServletRequest req) {
            super(req);
            put(req, "j_enc_np");
            put(req, "j_enc_cnp");
        }
    
        private void put(HttpServletRequest req, String key) {
            if (req.getParameter(key) != null && !"".equals(key)) {
                params.put(key, req.getParameter(key));
            }
        }
    
        public boolean isChange() {
            return !params.isEmpty();
        }
    
        public String get(String key) {
            return params.get(key);
        }
    
    }
    
    1. 准备登录页面以获取其他参数:

      <form name="f" id="form-login" action="<c:url value="login"/>" method="post">
      <fieldset>
              <p><label for="j_enc_u"><spring:message code="app.lbl.username" /></label></p>
              <p><input type="text" id="j_enc_u" name="j_enc_u"></p>
      
              <p><label for="j_enc_p"><c:choose><c:when test="${param.change}"><spring:message code="app.lbl.old.password" /></c:when><c:otherwise><spring:message code="app.lbl.password" /></c:otherwise></c:choose></label></p>
              <p><input type="password" id="j_enc_p" name="j_enc_p"></p>
      
              <c:if test="${param.change}">
                  <p><label for="j_enc_np"><spring:message code="app.lbl.new.password" /></label></p>
                  <p><input type="password" id="j_enc_np" name="j_enc_np"></p>
      
                  <p><label for="j_enc_cnp"><spring:message code="app.lbl.new.conf.password" /></label></p>
                  <p><input type="password" id="j_enc_cnp" name="j_enc_cnp"></p>
              </c:if>
      
              <p><input type="submit" value="<spring:message code="app.lbl.validate" />"></p>
          </fieldset>
      </form>
      
      <c:choose>
          <c:when test="${param.login_error}">
              <div class="error-message"><c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" /></div>
          </c:when>
      </c:choose>
      
    2. 准备AuthenticationProvider自定义:

      @Override
      public Authentication authenticate(Authentication authentication) throws AuthenticationException {
      
              final String username = authentication.getPrincipal().toString().trim();
              final String password = authentication.getCredentials().toString().trim();
      
              final String encryptedPassword = passwordEncryptor.encryptPassword(password);
              final CustomWebAuthenticationDetails cwad = (CustomWebAuthenticationDetails) authentication.getDetails();
      
              try {
                  final SecUserStatus status =
                      (cwad.isChange()) ?
                          authenticationService.resetPassword(username, encryptedPassword, cwad.get("j_enc_np"), cwad.get("j_enc_cnp")) :
                              authenticationService.authenticate(username, encryptedPassword, cwad.getRemoteAddress());
      
                  SecUser secUser = status.getSecUser();
      
                  final UsernamePasswordAuthenticationToken authenticationToken =
                          new UsernamePasswordAuthenticationToken(
                              secUser,
                              secUser.getPassword(),
                              secUser.getAuthorities());
      
                  authenticationToken.setDetails(secUser);
                  authenticationService.lastLogin(secUser.getId(), new Date());
                  return authenticationToken;
      
              } catch (AuthenticationException e) {
                  LOGGER.error(e.getMessage(), e);
                  throw e;
              }
      }