Registering a servlet filter in Apache Karaf

I have already written several articles about deploying web applications in OSGi, and in particular in Apache Karaf. This blog post is a small addition to indicate how to deploy a filter in such an environment.

My first attempt was trying to register my filter directly in the HTTP service. Unfortunately, there is no method for this. After searching the web, I finally found an example here. Apache Karaf comes with Pax-Web. And Pax-Web has extender modules. Basically, all you have to do is registering your filter as an OSGi service. Pax-Web extender modules will be notified about this service (white board pattern) and automatically register it as a servlet filter in the web server (which is Jetty, by the way).

Personnaly, I use this approach to add authentication to REST services. They are implemented with Jersey 1.x and served by Karaf’s web server. The filter intercepts requests and verifies they have the identity token. Obviously, it aims at being used with HTTPS.

So, here are some code snippets.
Let’s start with the filter itself.

public class AuthenticationFilter implements Filter {

	private final Logger logger = Logger.getLogger( getClass().getName());

	private AuthenticationManager authenticationMngr;
	private boolean authenticationEnabled;
	private long sessionPeriod;


	@Override
	public void doFilter( ServletRequest req, ServletResponse resp, FilterChain chain )
	throws IOException, ServletException {

		if( ! this.authenticationEnabled ) {
			chain.doFilter( req, resp );

		} else {
			HttpServletRequest request = (HttpServletRequest) req;
			HttpServletResponse response = (HttpServletResponse) resp;
			String requestedPath = request.getRequestURI();
			this.logger.info( "Path for auth: " + requestedPath );

			// Find the session ID in the cookies
			String sessionId = null;
			Cookie[] cookies = request.getCookies();
			if( cookies != null ) {
				for( Cookie cookie : cookies ) {
					if( UrlConstants.SESSION_ID.equals( cookie.getName())) {
						sessionId = cookie.getValue();
						break;
					}
				}
			}

			// Is there a valid session?
			boolean loggedIn = false;
			if( ! Utils.isEmptyOrWhitespaces( sessionId )) {
				loggedIn = this.authenticationMngr.isSessionValid( sessionId, this.sessionPeriod );
				this.logger.finest( "Session " + sessionId + (loggedIn ? " was successfully " : " failed to be ") + "validated." );
			} else {
				this.logger.finest( "No session ID was found in the cookie. Authentication cannot be performed." );
			}

			// Valid session, go on. Send an error otherwise.
			// No redirection, we mainly deal with our web socket and REST API.
			boolean loginRequest = requestedPath.endsWith( IAuthenticationResource.PATH + IAuthenticationResource.LOGIN_PATH );
			if( loggedIn || loginRequest ) {
				chain.doFilter( request, response );
			} else {
				response.sendError( 403, "Authentication is required." );
			}
		}
	}


	@Override
	public void destroy() {
		// nothing
	}


	@Override
	public void init( FilterConfig filterConfig ) throws ServletException {
		// nothing
	}

	public void setAuthenticationEnabled( boolean authenticationEnabled ) {
		this.authenticationEnabled = authenticationEnabled;
	}

	public void setAuthenticationManager( AuthenticationManager authenticationMngr ) {
		this.authenticationMngr = authenticationMngr;
	}

	public void setSessionPeriod( long sessionPeriod ) {
		this.sessionPeriod = sessionPeriod;
	}
}

And here is the section in my class that registers my filter.
It is the same method that registers my servlets in the HTTP service.

this.authenticationFilter = new AuthenticationFilter();
this.authenticationFilter.setAuthenticationEnabled( this.enableAuthentication );
this.authenticationFilter.setAuthenticationManager( this.authenticationMngr );
this.authenticationFilter.setSessionPeriod( this.sessionPeriod );

initParams = new Hashtable<> ();
initParams.put( "urlPatterns", "*" );

// Consider the bundle context can be null (e.g. when used outside of OSGi)
if( this.bundleContext != null )
	this.filterServiceRegistration = this.bundleContext.registerService( Filter.class, this.authenticationFilter, initParams );
else
	this.logger.warning( "No bundle context was available, the authentication filter was not registered." );

In Karaf 4.x, this is enough to register a servlet filter.
There are other alternatives, but this one requires less code. Read Achim’s answer here for more details.


About this entry