These days I tried to integrate a custom login page into my Web Application. I found a lot of hints in the Web
using a a plain form tag for the Login page but nothing was working and I always got a errors like “No navigation case match for viewId action j_spring_security_check and outcome…” I didn’t get it work by using the j_security_check mechanism, therefore I used a custom bean and controller.
Here is the solution:
my web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml,
/WEB-INF/applicationsContext-Security.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<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 applicationContext.xml:
<bean scope="session" id="loginBean" class="com.XXX.security.LoginBean">
<aop:scoped-proxy />
</bean>
<bean scope="singleton" id="loginController"
class="com.XXX.security.LoginController">
<property name="loginBean" ref="loginBean" />
<property name="authenticationManager" ref="authenticationManager" />
</bean>
the applicationContext-Security.xml:
<sec:http auto-config='true' use-expressions="true">
<sec:intercept-url pattern="/xhtml/loggedout.xhtml" access="permitAll" />
<sec:intercept-url pattern="/xhtml/timeout.xhtml" access="permitAll" />
<sec:intercept-url pattern="/xhtml/login.xhtml" access="permitAll" />
<sec:intercept-url pattern="/xhtml/**" access="isAuthenticated()" />
<sec:form-login login-page="/xhtml/login.xhtml" />
</sec:http>
<!-- AuthenticationManager - add your User-Service-->
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="XXXXXX">
<password-encoder hash="md5" />
</authentication-provider>
</authentication-manager>
the Form: /xhtml/login.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:my="http://www.XXXX.com/my/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:security="http://www.springframework.org/security/facelets/tags">
<my:myMainTemplate title="Login" class="mainContent" >
<h:form id="loginForm" prependId="false">
<h:panelGrid columns="2">
<h:outputLabel for="j_username" value="User: " />
<h:inputText id="j_username" value="#{loginBean.userName}" required="true" requiredMessage="Username !"/>
<h:outputLabel for="j_password" value="Password: " />
<h:inputSecret id="j_password" value="#{loginBean.password}" required="true" requiredMessage="Passwort !"/>
<h:commandButton id="submitButton" value="Start" action="#{loginController.login}" />
</h:panelGrid>
</h:form>
</my:myMainTemplate>
</html>
my beans – loginBean.java:
package com.XXXX.security;
import java.io.Serializable;
public class LoginBean implements Serializable{
private static final long serialVersionUID = 6175255537226165236L;
private String userName;
private String password;
public void reset(){
userName = "";
password = "";
}
... getter/setter...
}
and loginController.java
package com.XXXX.security;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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;
public class LoginController {
private static final Log LOG = LogFactory.getLog(LoginController.class);
// beans used by this controller, injected by spring
private LoginBean loginBean;
private AuthenticationManager authenticationManager;
/**
* the login action called by the view
* @return
*/
public String login() {
try{
LOG.info("Login started for User with Name: "+getLoginBean().getUserName());
// check if userdata is given
if (getLoginBean().getUserName() == null || getLoginBean().getPassword() == null) {
FacesMessage facesMsg = new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Error", "login.failed" );
FacesContext.getCurrentInstance().addMessage(null, facesMsg);
LOG.info("Login not started because userName or Password is empty: "+getLoginBean().getUserName());
return null;
}
// authenticate afainst spring security
Authentication request = new UsernamePasswordAuthenticationToken(
getLoginBean().getUserName(), getLoginBean().getPassword());
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
} catch (AuthenticationException e) {
LOG.info("Login failed: " + e.getMessage());
FacesMessage facesMsg = new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Error", "login.failed") ;
FacesContext.getCurrentInstance().addMessage(null, facesMsg);
return null;
}
return "success";
}
/**
* @return the loginBean
*/
public LoginBean getLoginBean() {
return loginBean;
}
/**
* @param loginBean the loginBean to set
*/
public void setLoginBean(LoginBean loginBean) {
this.loginBean = loginBean;
}
/**
* @return the authenticationManager
*/
public AuthenticationManager getAuthenticationManager() {
return authenticationManager;
}
/**
* @param authenticationManager the authenticationManager to set
*/
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
}
and Last but not least, add navigationRules to your
faces-config.xml:
<navigation-rule>
<from-view-id>/xhtml/login.xhtml</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/xhtml/main/main.xhtml</to-view-id>
</navigation-case>
</navigation-rule>