Custom Loginpage with SpringSecurity 3, JSF2 and Facelets

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>

log4j doesn’t refresh Loglevels correctly

I don’t know whether it is a log4j (JBoss 1.2.14) problem or a bug in the Jboss Log4jService URLWatchTimerTask (from JBoss 4.3.0.GA) which i use on my server to refresh the log4j configuration automatically after a change. When I try to  set a higher log level by a base package name, it doesn’t work if formerly a lower log level was set explicit for a class.

Here is an example:

First my machine is configured like this (snip of log4j.xml):

<!-- limit my code-->
<category name="de.koelnerwasser">
 <priority value="INFO" />
</category>
<!-- log a class explicit -->
<category name="de.koelnerwasser.testclass">
 <priority value="DEBUG" />
</category>

Until now everything is fine – all info messages and the debug messages of testclass are logged.  When i change the log4j.xml like this:

<!-- limit my code-->
<category name="de.koelnerwasser">
 <priority value="ERROR" />
</category>

.. and deploy the file to my server, the log4j framework will be reconfigured by the URLWatchTimerTask.

Now only error will be logged and – this is curious: Debug Messages of my testclass . The old loglevel DEBUG will not be overwritten by the ERROR level.  I think this is a little bug of log4j or the Jboss URLWatchTimerTask, i will analyse the code and add a defect to Jboss or Apache.

PortletStateHolder in the JBoss PortletBridge and ConcurrentModificationException

Today I run again into my old problem with the PortletStateHolder. While he is under fire he throws a lot of ConcurrentModificationExceptions.

Here is one of these Exceptions:

Caused by: java.util.ConcurrentModificationException
at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:373)
at java.util.LinkedHashMap$KeyIterator.next(LinkedHashMap.java:384)
at de.koelnerwasser.portlet.bridge.MyPortletStateHolder
         .removeSessionStates(MyPortletStateHolder.javaa:198)

Because I synchronized every write access to the class variable “states” eg.:

public void addWindowState(StateId stateId, PortletWindowState state) {
  // prevent concurrent access
  synchronized (states) {
    states.put(stateId, state);
  }
 }

I wonder about the exceptions.  So I investigated the read access to “states” in the “getWindowState()” method:

public PortletWindowState getWindowState(StateId stateId) {
 PortletWindowState state = null;
  if (null != stateId) {
   state = states.get(stateId);
  }
  return state;
 }

I found that states.get() calls a put on the org.apache.commons.collections.LRUMap. Here is the code:

public Object get(Object key)
{
  if(!containsKey(key))
  {
    return null;
  } else {
    Object value = remove(key);
    super.put(key, value);
    return value;
  }

The put() call causes the ConcurrentModificationException. The solution is to synchronize every call on the state LRUMap.