我在我的jsf2网络应用程序中使用spring security 3.
如何在登录表单中显示错误的凭据消息,而不将get param?login_error附加到authenticated-fail-login-page?
我尝试使用像本教程那样的阶段监听器:
http://tutorials.slackspace.de/tutorial/Custom-login-page-with-JSF-and-Spring-Security-3
但它不起作用。
无法使用preRenderView侦听器。
并且都没有检查弹簧安全性最后一个异常以呈现消息。
有什么想法吗?
更新
我的登录页面:
<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>
答案 0 :(得分: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;" />
<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
声明为Spring安全配置文件中的身份验证失败事件的自定义处理程序(请注意,我还要显示我对身份验证成功处理程序执行相同操作,但你可能也可能不想这样做)。
<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>
答案 1 :(得分:1)
一个简单的解决方案是创建一个实现AuthenticationFailureHandler的spring bean
@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Autowired
private ServletContext context;
@Override
public void onAuthenticationFailure(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
request.setAttribute("message", exception.getMessage());
RequestDispatcher dispatcher = context.getRequestDispatcher("/login.xhtml");
dispatcher.forward(request, response);
}
}
然后配置spring security以使用它:
@Autowired
CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
...
http.formLogin().loginPage("/login.xhtml").permitAll()
.failureUrl("/login.xhtml?error=true")
.failureHandler(customAuthenticationFailureHandler)
...
最后,在login.xhtml中,使用此代码显示消息(如果您不使用质数,只需根据您的上下文调整代码即可):
<p:outputPanel rendered="#{not empty request.getAttribute('message')}" class="ui-messages ui-widget" >
<div class="ui-messages-error ui-corner-all">
<span class="ui-messages-error-icon">
</span>
<ul>
<li role="alert" aria-atomic="true">
<span class="ui-messages-error-summary">#{request.getAttribute('message')}</span>
</li>
</ul>
</div>
</p:outputPanel>