2012-01-18 42 views
3

我在我的jsf2 web應用程序中使用spring security 3。jsf彈簧安全登錄錯誤消息

如何在我的登錄表單中顯示錯誤的憑據消息,而不將get參數?login_error附加到authenticated-fail-login-page?

我用相聽衆喜歡這個教程試圖說:

http://tutorials.slackspace.de/tutorial/Custom-login-page-with-JSF-and-Spring-Security-3

但它不工作。

既不帶有preRenderView偵聽器。

既不檢查彈簧安全性,也不顯示消息的最後一個異常。

任何想法?

UPDATE:

我的登錄頁面:

<f:metadata> 
<f:viewParam name="error" value="#{autenticacionController.error}" /> 
<f:event listener="#{autenticacionController.comprobarAuthException}" type="preRenderView" /> 
</f:metadata> 
<h:messages globalOnly="true" layout="table" /> 
<h:form id="formLogin" prependId="false"> 
<h:outputLabel for="j_username" value="Usuario:" /> 
<h:inputText id="j_username" value="#{autenticacionController.administrador.login}" /> 
<h:outputLabel for="j_password" value="Contraseña:" /> 
<h:inputSecret id="j_password" value="#{autenticacionController.administrador.password}" /> 
<h:commandButton value="Entrar" action="#{autenticacionController.loginAction}" /> 
<h:commandButton value="Cancelar" immediate="true" action="#{autenticacionController.cancelarAction}" /> 
</h:form> 

我管理的bean:

@ManagedBean(name="autenticacionController") 
@RequestScoped 
public class AutenticacionController extends BaseController { 

    //entidad "administrador" contra el que validar los campos del form login 
    private Administrador administrador = new Administrador(); 

    //propiedad de spring-security (true si el usuario no es anónimo) 
    @SuppressWarnings("unused") 
    private boolean autenticado; 

    //propiedad para guardar el param GET si hubo fallo en la autenticación de SS 
    private int error; 

    //Constructor vacío del Backing Bean controlador 
    public AutenticacionController() { 
     log.info("Creación del backing bean AutenticacionController"); 
    } 

    @PostConstruct 
    public void init() { 
     //inicializar atributos del backing bean 
     log.info("PostConstruct del backing bean BarcoController"); 
    } 


    //Getters y setters de atributos del backing bean 
    public Administrador getAdministrador() { 
     return administrador; 
    } 
    public void setAdministrador(Administrador administrador) { 
     this.administrador = administrador; 
    } 

    public boolean isAutenticado() { 
     Authentication autenticacion = SecurityContextHolder.getContext().getAuthentication(); 
     boolean resultado = (autenticacion != null) && 
          !(autenticacion instanceof AnonymousAuthenticationToken) && 
          autenticacion.isAuthenticated(); 
     return resultado; 
    } 

    public int getError() { 
     return error; 
    } 
    public void setError(int error) { 
     this.error = error; 
    } 

    //MÉTODO LISTENER del evento preRenderView en la página login. 
    //Para comprobar si la autenticación de Spring Security falló (error=1). 
    //En ese caso muestra el error con un faces message. 
    public void comprobarAuthException (ComponentSystemEvent event){ 
     log.info("listener comprobarAuth"); 
     if (error==1) { 
      String msj = ""; 
      Exception e = (Exception) UtilJsf.getParamSessionMap(WebAttributes.AUTHENTICATION_EXCEPTION); 
      log.info("SSexception = "+((e==null)?"null":e.getMessage())); 
      if (e != null) { 
       String ultimoUsuario = (String) UtilJsf.getParamSessionMap(WebAttributes.LAST_USERNAME); 
       log.info("SS last_username = "+ultimoUsuario); 
       administrador.setLogin(ultimoUsuario); 
       if (e instanceof BadCredentialsException) { 
        msj = UtilJsf.getMsjProperties("msjsInfo", "UsuPwdIncorrectos"); 
       } else { 
        msj = UtilJsf.getMsjProperties("msjsInfo", "ErrorAutenticacion"); 
       } 
       UtilJsf.mostrarFacesMsjGlobal(msj); 
      } 
     } 
     return; 
    } 


    /* ******************************* */ 
    /* Métodos "action" del form login */ 
    /* ******************************* */ 

    // EVENTO: Pulsar el botón "entrar" del form login 
    // Reenviar(FORWARD) la petición a la URL "/j_spring_security_check" para autenticarse 
    // También se debe configurar el filtro de spring-security para que procese forwards 
    public void loginAction() { 
     try { 
      FacesContext.getCurrentInstance().getExternalContext().dispatch("/j_spring_security_check"); 
     } catch (IOException e) { 
     } 
    } 

    // EVENTO: Pulsar el boton "cancelar" en el form login 
    // No hacer nada --> Ir a la pantalla de inicio de la aplic 
    public String cancelarAction() { 
     return "/inicio"; 
    } 

} 

在我的Spring Security的配置,我有:

authentication-failure-url="/faces/paginas/autenticacion/login.xhtml?error=1" 

如果我從登錄頁面和監聽器中刪除錯誤參數和viewParam,我只是檢查Spring Security異常,它不起作用。

感謝註銷,我爲它的做法是相似的,我有以下鏈接它:

<h:outputLink value="#{request.contextPath}/j_spring_security_logout" rendered="#{autenticacionController.autenticado}">Cerrar sesión (Administrador)</h:outputLink> 

回答

7

我傾向於使用上preRenderView的<f:event>將更新我的表格上的信息組件。這是我做到的。

<f:event listener="#{loginBean.updateMessages(true)}" type="preRenderView" /> 
     <div style="margin-left: 50px; width: 500px;"><br /> 
     <h:form id="loginForm" prependId="false"> 

      <p:messages id="errorMessages" /> 
      <label for="j_username"> 
       <h:outputText value="Username:" /><br /> 
      </label> 
      <h:inputText id="j_username" required="true" width="500" style="width: 300px;" /> 

      <br /> 
      <br /> 
      <label for="j_password"> 
       <h:outputText value="Password:" /><br /> 
      </label> 
      <h:inputSecret id="j_password" required="true" width="500" style="width: 300px;" /> 
      &nbsp;<h:link value="Forgot my password" outcome="forgotpassword" /> 
      <br /> 
      <br /> 
      <label for="_spring_security_remember_me"> 
       <h:outputText value="Remember me" /> 
      </label> 
      <h:selectBooleanCheckbox id="_spring_security_remember_me" /> 
      <br /><br /> 
      <p:commandButton ajax="false" type="submit" id="login" action="#{loginBean.doLogin}" value="Login" update="errorMessages" /> 
     </h:form> 
     </div> 

然後在我的LoginBean託管bean中,我將請求轉發到Spring Security servlet,並更新消息。您會注意到,如果您有興趣瞭解我是如何處理該問題的,那麼我也有註銷操作的代碼。

private String username; 
private String password;  

public String getUsername() { 
    return username; 
} 

public void setUsername(final String username) { 
    this.username = username.trim(); 
} 

public String getPassword() { 
    return password; 
} 

public void setPassword(final String password) { 
    this.password = password.trim(); 
} 

public void updateMessages(boolean update) throws Exception { 
    System.out.println("Start LoginBean.updateMessages"); 
    ex = (Exception)FacesContext.getCurrentInstance().getExternalContext().getSessionMap() 
     .get(WebAttributes.AUTHENTICATION_EXCEPTION); 

if (ex != null) { 
    log.error("Authentication Failed! ", ex); 
    System.err.println("Authentication Failed! " + ex.getMessage()); 
    FacesContext.getCurrentInstance().addMessage(null, 
      new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), ex.getMessage())); 
} 
    System.out.println("End LoginBean.updateMessages"); 
} 

public String doLogin() { 
    log.info("Start LoginBean.doLogin"); 
    try { 
     ExternalContext context = FacesContext.getCurrentInstance().getExternalContext(); 

     RequestDispatcher dispatcher = ((ServletRequest) context.getRequest()) 
       .getRequestDispatcher("/j_spring_security_check"); 

     dispatcher.forward((ServletRequest) context.getRequest(), 
       (ServletResponse) context.getResponse()); 

     FacesContext.getCurrentInstance().responseComplete(); 
     // It's OK to return null here because Faces is just going to exit. 
    } catch (Exception e) { 
     log.error("Exception doLogin", e); 
    } finally { 
     log.info("End LoginBean.doLogin"); 
    } 
return ""; 
} 

public String logout() { 
    FacesContext context = FacesContext.getCurrentInstance(); 
    Map<String, Object> sessionMap = context.getExternalContext().getSessionMap(); 
    if (!sessionMap.containsKey("sessionBean")) 
     return ""; 

    SessionBean sessionBean = (SessionBean)sessionMap.get("sessionBean"); 
    log.info("Logging out user: " + sessionBean.getLoggedInUser().getUsername()); 

    sessionMap.remove("sessionBean"); 

    //HttpSession session = (HttpSession)context.getExternalContext().getSession(false); 
    //session.invalidate(); 
RequestDispatcher dispatcher = ((ServletRequest) context.getExternalContext().getRequest()) 
     .getRequestDispatcher("/j_spring_security_logout"); 

try { 
     dispatcher.forward((ServletRequest) context.getExternalContext().getRequest(), 
       (ServletResponse) context.getExternalContext().getResponse()); 
    } catch (ServletException e) { 
     log.error("ServletException", e); 
    } catch (IOException e) { 
     log.error("IOException", e); 
    } 

FacesContext.getCurrentInstance().responseComplete(); 
// It's OK to return null here because Faces is just going to exit. 

log.info("End LoginBean.logout");  
return ""; 
} 

public boolean isLoggedIn() { 
    FacesContext context = FacesContext.getCurrentInstance(); 
    Map<String, Object> sessionMap = context.getExternalContext().getSessionMap(); 
    return sessionMap.containsKey("sessionBean"); 
} 

編輯:

我想我最好現在明白你的問題。我記得我的麻煩來此工作過,所以基本上我不得不寫我自己的類實現AuthenticationFailureHandler和妥善實施方法:

@Override 
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) throws IOException, ServletException { 
    //Do business logic stuff, logging, etc... 
    request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, ex); 
    response.sendRedirect("login.xhtml"); 

基本上你看到,我實例化一個例外,並將其設置爲會話屬性,以便稍後在我的託管bean中可以檢索並轉換爲FacesMessage

您還必須聲明此AuthenticationFailureHandler爲您的春季安全配置文件(請注意,我也表示我做同樣的事情,對認證成功處理程序,認證失敗事件自定義處理程序,但你可能會或也可能不想這樣做)

<form-login login-page="/login.xhtml" login-processing-url="/j_spring_security_check" 
     authentication-success-handler-ref="authenticationSuccessBean" 
     authentication-failure-handler-ref="authenticationFailureBean" /> 

... 
<beans:bean id="authenticationFailureBean" class="com.maple.controllers.FailureHandler"> 
    <beans:property name="userBo" ref="userController" /> <!-- Just injecting my BL layer... --> 
</beans:bean> 
+0

謝謝你的回答。我的方法與您的方法類似,但如果我不將錯誤參數添加到已驗證的失敗登錄頁面,則不起作用。我使用preRenderView監聽器,但在方法中檢查錯誤參數。如果我不使用該參數,只檢查彈簧安全異常,則不起作用。我編輯我的問題添加我的代碼,以便您可以看到。對不起,如果我的英語不是很好,但我來自西班牙。 – choquero70 2012-01-19 22:09:44

+0

@ choquero70我更新了我的答案,並提供了更多詳細信息......如果您瞭解,請告訴我。 – 2012-01-20 02:19:06

+0

謝謝maple_shaft。我想我或多或少地理解你在說什麼,但我必須檢查它。雖然我不明白爲什麼有必要在你的onAuthenticationFailure方法中使用Spring Security異常作爲會話屬性(Spring Security被認爲是這樣做的,但是令人驚訝的是,當我在preRenderView監聽器中檢查它時,它是空的)。 – choquero70 2012-01-22 20:50:22