[[_servlet_filter_adapter]]
==== Java servlet filter adapter

include::adapter-deprecation-notice.adoc[]

If you are deploying your Java Servlet application on a platform where there is no {project_name} adapter you opt to use the servlet filter adapter.
This adapter works a bit differently than the other adapters. You do not define security constraints in web.xml.
Instead you define a filter mapping using the {project_name} servlet filter adapter to secure the url patterns you want to secure.

WARNING: Backchannel logout works a bit differently than the standard adapters.
Instead of invalidating the HTTP session it marks the session id as logged out.
There's no standard way to invalidate an HTTP session based on a session id.

[source,xml]
----
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0">

	<module-name>application</module-name>

    <filter>
        <filter-name>Keycloak Filter</filter-name>
        <filter-class>org.keycloak.adapters.servlet.KeycloakOIDCFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Keycloak Filter</filter-name>
        <url-pattern>/keycloak/*</url-pattern>
        <url-pattern>/protected/*</url-pattern>
    </filter-mapping>
</web-app>
----

In the snippet above there are two url-patterns.
 _/protected/*_ are the files we want protected, while the _/keycloak/*_ url-pattern handles callbacks from the {project_name} server.

If you need to exclude some paths beneath the configured `url-patterns` you can use the Filter init-param `keycloak.config.skipPattern` to configure
a regular expression that describes a path-pattern for which the keycloak filter should immediately delegate to the filter-chain.
By default no skipPattern is configured.

Patterns are matched against the `requestURI` without the `context-path`. Given the context-path `/myapp` a request for `/myapp/index.html` will be matched with `/index.html` against the skip pattern.

[source,xml]
----
<init-param>
    <param-name>keycloak.config.skipPattern</param-name>
    <param-value>^/(path1|path2|path3).*</param-value>
</init-param>
----

Note that you should configure your client in the {project_name} Admin Console with an Admin URL that points to a secured section covered by the filter's url-pattern.

The Admin URL will make callbacks to the Admin URL to do things like backchannel logout.
So, the Admin URL in this example should be `http[s]://hostname/{context-root}/keycloak`.

If you need to customize the session ID mapper, you can configure the fully qualified name of the class in the Filter init-param keycloak.config.idMapper. Session ID mapper is a mapper that is used to map user IDs and session IDs. By default org.keycloak.adapters.spi.InMemorySessionIdMapper is configured.

[source,xml]
----
<init-param>
    <param-name>keycloak.config.idMapper</param-name>
    <param-value>org.keycloak.adapters.spi.InMemorySessionIdMapper</param-value>
</init-param>
----

The {project_name} filter has the same configuration parameters as the other adapters except you must define them as filter init params instead of context params.

To use this filter, include this maven artifact in your WAR poms:

[source,xml,subs="attributes+"]
----
<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-servlet-filter-adapter</artifactId>
    <version>{project_versionMvn}</version>
</dependency>
----
ifeval::[{project_community}==true]
===== Using on OSGi

The servlet filter adapter is packaged as an OSGi bundle, and thus is usable in a generic OSGi environment (R6 and above) with HTTP Service and HTTP Whiteboard.

====== Installation

The adapter and its dependencies are distributed as Maven artifacts, so you'll need either working Internet connection to access Maven Central, or have the artifacts cached in your local Maven repo.

If you are using Apache Karaf, you can simply install a feature from the Keycloak feature repo:

[source,subs="attributes+"]
----
karaf@root()> feature:repo-add mvn:org.keycloak/keycloak-osgi-features/{project_versionMvn}/xml/features
karaf@root()> feature:install keycloak-servlet-filter-adapter
----

For other OSGi runtimes, please refer to the runtime documentation on how to install the adapter bundle and its dependencies.

NOTE: If your OSGi platform is Apache Karaf with Pax Web, you should consider using https://access.redhat.com/products/red-hat-single-sign-on/[Red Hat Single Sign-On] 7.x JBoss Fuse 7 adapters instead.

====== Configuration

First, the adapter needs to be registered as a servlet filter with the OSGi HTTP Service. The most common ways to do this are programmatic (for example via bundle activator) and declarative (using OSGi annotations).
We recommend using the latter since it simplifies the process of dynamically registering and un-registering the filter:

[source,java]
----
package mypackage;

import javax.servlet.Filter;
import org.keycloak.adapters.servlet.KeycloakOIDCFilter;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;

@Component(
    immediate = true,
    service = Filter.class,
    property = {
        KeycloakOIDCFilter.CONFIG_FILE_PARAM + "=" + "keycloak.json",
        HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN + "=" +"/*",
        HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT + "=" + "(osgi.http.whiteboard.context.name=mycontext)"
    }
)
public class KeycloakFilter extends KeycloakOIDCFilter {
  //
}
----

The above snippet uses OSGi declarative service specification to expose the filter as an OSGI service under `javax.servlet.Filter` class.
Once the class is published in the OSGi service registry, it is going to be picked up by OSGi HTTP Service implementation and used for filtering requests for the specified servlet context. This will trigger Keycloak adapter for every request that matches servlet context path + filter path.

Since the component is put under the control of OSGi Configuration Admin Service, it's properties can be configured dynamically.
To do that, either create a `mypackage.KeycloakFilter.cfg` file under the standard config location for your OSGi runtime:
[source]

----
keycloak.config.file = /path/to/keycloak.json
osgi.http.whiteboard.filter.pattern = /secure/*
----

or use interactive console, if your runtime allows for that:

[source]
----
karaf@root()> config:edit mypackage.KeycloakFilter
karaf@root()> config:property-set keycloak.config.file '${karaf.etc}/keycloak.json'
karaf@root()> config:update
----

If you need more control, for example, providing custom `KeycloakConfigResolver` to implement <<_multi_tenancy,multi tenancy>>, you can register the filter programmatically:

[source,java]
----
public class Activator implements BundleActivator {

  private ServiceRegistration registration;

  public void start(BundleContext context) throws Exception {
    Hashtable props = new Hashtable();
    props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, "/secure/*");
    props.put(KeycloakOIDCFilter.CONFIG_RESOLVER_PARAM, new MyConfigResolver());

    this.registration = context.registerService(Filter.class.getName(), new KeycloakOIDCFilter(), props);
  }

  public void stop(BundleContext context) throws Exception {
    this.registration.unregister();
  }
}
----

Please refer to https://github.com/apache/felix-dev/tree/master/http#using-the-osgi-http-whiteboard[Apache Felix HTTP Service] for more info on programmatic registration.

endif::[]
