In a previous post I showed you how to add Spring Security to your application.
In this continuation I’m going to show how to integrate JSF and Spring Security 3.
At the end, we’ll have a custom JSF login page.
The login page will continue to be shown until a successful authentication will occur.
Spring Security’s authentication error exception will be displayed as a JSF h:messages in the login page itself.
Our login page:
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core"%> <%@ taglib prefix="c_rt" uri="http://java.sun.com/jstl/core_rt"%> <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core"%> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html"%> <html> <head> <title>Spring Security Login</title> </head> <body> <f:view > <h:form > <h:panelGrid columns="2"> <h:outputLabel value="User Name" for="j_username"/> <h:inputText id="j_username" value="#{loginBean.username}" required="true"/> <h:outputLabel value="Password" for="j_password"/> <h:inputSecret id="j_password" value="#{loginBean.password}" required="true"/> </h:panelGrid> <h:commandButton action="#{loginBean.doLogin}" value="Login"/> <h:messages style="color: red;"/> </h:form> </f:view> </body> </html>
How our login page will look like:
Changes to be made to web.xml:
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> <dispatcher>FORWARD</dispatcher> <dispatcher>REQUEST</dispatcher> </filter-mapping>
The backing bean LoginBean.java:
package com.tutorial.backingbeans; import java.io.IOException; import javax.faces.application.FacesMessage; import javax.faces.bean.RequestScoped; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.authentication.AbstractProcessingFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import com.tutorial.util.FacesUtils; public class LoginBean { private String username; private String password; public LoginBean() { } public String doLogin() throws IOException, ServletException { ExternalContext context = FacesContext.getCurrentInstance() .getExternalContext(); RequestDispatcher dispatcher = ((ServletRequest) context.getRequest()) .getRequestDispatcher("/j_spring_security_check?j_username=" + username + "&j_password=" + password); 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. return null; } public String getPassword() { return password; } public String getUsername() { return username; } public void setPassword(String password) { this.password = password; } public void setUsername(String username) { this.username = username; } }
Changes to be made to applicationContext-secutiry.xml:
<form-login login-page="/login.jsf" default-target-url="/loginSuccess.jsp" />
LoginErrorPhaseListener.java:
This listener will capture Spring Security’s exceptions and show them in the login page as JSF messages.
package com.tutorial.listener; import javax.faces.context.FacesContext; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.authentication.AbstractProcessingFilter; import com.tutorial.util.FacesUtils; public class LoginErrorPhaseListener implements PhaseListener { private static final long serialVersionUID = -1216620620302322995L; @Override public void afterPhase(final PhaseEvent arg0) {} @Override public void beforePhase(final PhaseEvent arg0) { Exception e = (Exception) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get( AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY); if (e instanceof BadCredentialsException) { FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put( AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, null); FacesUtils.addErrorMessage("Username or password not valid."); } } @Override public PhaseId getPhaseId() { return PhaseId.RENDER_RESPONSE; } }
We have to add this listener to faces-config.xml:
<lifecycle> <phase-listener>com.tutorial.listener.LoginErrorPhaseListener</phase-listener> </lifecycle>
FacesUtils.java
package com.tutorial.util; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; public class FacesUtils { public static void addErrorMessage(String msg) { addMessage(FacesMessage.SEVERITY_ERROR, msg); } private static void addMessage(FacesMessage.Severity severity, String msg) { final FacesMessage facesMsg = new FacesMessage(severity, msg, msg); FacesContext.getCurrentInstance().addMessage(null, facesMsg); } public static void addSuccessMessage(String msg) { addMessage(FacesMessage.SEVERITY_INFO, msg); } public static String getBundleKey(String bundleName, String key) { return FacesContext .getCurrentInstance() .getApplication() .getResourceBundle(FacesContext.getCurrentInstance(), bundleName).getString(key); } }
2 Responses to Spring Security 3 + JSF integration
Leave a Reply
You must be logged in to post a comment.
Where are your authetication Provider in springsecurity.xml?
It’s in this post.