Using Jakarta Security on Tomcat and the Payara Platform
Published on 18 Jul 2019by Arjan Tijms
Java EE Security API is one of the new APIs in Java EE 8. With Java EE currently being transferred and rebranded to Jakarta EE, this API will soon be rebranded to Jakarta Security, which is the term we'll use in this article. Jakarta Security is part of the Jakarta APIs, included and active in the Payara Platform by default with no configuration required in order to use it. With some effort, Jakarta Security can be used with Tomcat, as well.
￼Jakarta Security is a powerful, but simple to use API that takes away some of the obscurity present in the APIs that predate it. The API focusses on being fully standardised (not depending on proprietary server config) and recognising the use case to configure security from within an application archive. The latter is specifically useful for cloud deployments, where lightweight and self-sufficient war archives can be deployed to generic Jakarta EE compatible containers.
Jakarta Security is included in the Payara Platform by default and is also active by default. As a result, there is nothing that must be included in a war archive to use Jakarta Security in the Payara Platform, and nothing to configure in order for code to use it. Payara uses Soteria, which is the reference implementation under Java EE, but just one of the possible implementations under Jakarta, which did away with the RI concept. Another implementation is available from our friends at Open Liberty, and an implementation from TomEE, aptly called TomEE Security is currently underway.
One of the things that sets Soteria apart is that it was designed with the usage of standard APIs in mind and the ability to run on different servers. Any functionality Soteria requires that isn't available in standard APIs is delegated to an SPI with a default implementation covering a series of well-known servers. This SPI is internal in Soteria 1.0.x, but will be made public in Soteria 1.1.
Adding Soteria to Tomcat
With Jakarta Security being very much based on Jakarta EE, a valid question to ask is; does it also run on Tomcat? The answer is yes, but with some caveats.
Jakarta Security depends on Servlet (with the Servlet Container Profile JASPIC extensions), Expression Language and CDI. Soteria additionally depends on JACC, but this is abstracted via its SPI:
Tomcat obviously already implements Servlet (with the Servlet Container Profile JASPIC extensions), but also Expression Language. To start getting it to run we first need to add CDI to Tomcat. This basically entails adding a CDI implementation, where Weld is quite easy to by adding the following to pom.xml in a Maven project:
Subsequently we also need to register the CDI bean manager in JNDI by adding a META-INF/context.xml file with the following contents:
Next we'll add the Soteria implementation:
We don't necessarily have to include the respective API dependencies (CDI and Jakarta Security) as they are referenced by the implementations, and therefor automatically fetched by Maven.
The above should be enough to get things running (we'll come to the JACC dependency later), if only it wasn't for a tiny, but crucial annoyance of the bean manager being stored under "java:comp/env/BeanManager" instead of the normal "java:comp/BeanManager". This is a rather well known "trap", and many projects have adjusted for this, but Soteria hasn't yet. Alas (Soteria 1.1 will certainly include a fix for this).
In the meantime what we can do is using a handy trick in Servlet, where we can override classes coming from embedded jars (jars in WEB-INF/lib). To do this, we copy the CdiUtils.java file from the Soteria source to a package with the same name in our project, and then in that file change the getBeanManager() method to:
With this all in place we can now code up a simple protected Servlet, using the provided BASIC authentication mechanism:
What we are seeing here is a fairly standard Servlet, with the only extra bit the Jakarta Security annotation for defining the BasicAuthenticationMechanism. Note that while the annotation is put on the Servlet class in this example, it's a global annotation that sets the authentication mechanism for the entire application. Just setting the authentication mechanism from an application may not seem terribly exciting at first sight (one can do this using web.xml as well), but the main difference here is that the authentication mechanism is a CDI bean with a well-defined interface, which opens up lots of options with regards to intercepting, decorating and replacing said bean.
Something which can't really be done with plain Servlets and only in a fairly obscure way with the Servlet Container Profile JASPIC extensions on Tomcat is defining the database within the application which is used to validate the credentials and return groups (which map 1:1 to roles by default). With Jakarta Security this is trivial:
Running this on Tomcat, we see the various bits and pieces being initialised and picked up in the server log:
Deploying and running this on Tomcat will present us with a well known browser authentication dialog, and then if everything went right the following output:
A fully working example is available in the Vendor EE Samples project.
Adding a JWT Authentication Mechanism
An important aspect of Jakarta Security vs, native Servlet security authentication mechanisms is that the same APIs that are used by the build-in mechanisms are available to build third party ones on as well. For instance, the MicroProfile JWT authentication mechanism can be based on Jakarta Security, and in fact two implementations do: Payara MP JWT and SmallRye MP JWT. This therefor makes it potentially possible to add those to Tomcat.
We'll be looking at Payara MP JWT for this, which was the first MP JWT implementation building completely on the standard APIs only*, at a time it perhaps wasn't fully realised this was even possible. *) (with one exception for an integration class present)
Payara MP JWT however is (currently) not published on Maven central. It can be build from source though by cloning email@example.com:payara/Payara.git and building it using `mvn clean install` . MP JWT does have its own additional dependencies, which in terms of APIs are JSON-P and MP Config. For Tomcat we have to add implementations for those as well, and we'll do that by adding SmallRye Config and the GlassFish JSON-P implementation. This will make the dependencies we list in our pom.xml quite a bit longer, although the implementations actually depend on the APIs, so we don't really need to list them (being transitive dependencies Maven will fetch them). Our pom dependency section will then look as follows:
With this in place we can start using MP JWT on Tomcat. As an example application we will modify an existing MicroProfile sample to make it Tomcat compatible. Without repeating all the existing MP JWT tutorials here, in short using MP JWT means setting the authentication mechanism by placing the @LoginConfig annotation somewhere, and then configuring a public key inside the application that will be used to verify incoming JWT tokens. For the request we sign a JWT token with the corresponding private key and include it in an "authorisation" header, for instance using wget:
wget --header="Authorization: Bearer eyJraWQiOiJcL3ByaXZhdGVLZXkucGVtIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiIyNDQwMDMyMCIsImF1ZCI6InM2QmhkUmtxdDMiLCJ1cG4iOiJ0ZXN0IiwiYXV0aF90aW1lIjoxNTYxNDYwOTEwLCJpc3MiOiJvcmcuZWNsaXBzZS5taWNyb3Byb2ZpbGUxMiIsImdyb3VwcyI6WyJhcmNoaXRlY3QiLCJtYXN0ZXIiLCJsZWFkZXIiLCJkZXYiXSwiZXhwIjoxNTcxNDYwOTEwLCJpYXQiOjE1NjE0NjA5MTAsImp0aSI6ImEtMTIzIn0.RLxhWgjyMN39W-3Ls-ppqAJvOeeaUmb4Y8NXKbhjufdAjgkA6x8OhAURQK22z1rBnMM5HMuRWF9uIEbgA7I4A5kdLfiDidEFZsIYFEJEzKBjDHN8Ind5kWY63CppAnTYhJYZ7oN2yfJ7wjfRQLllTBUY59YiZM-yuMEkOhgC9Tk6EpG1Xf390EmhoS7w8DokN89Q5ANrZtcFpIrOQChq-RW60QeKfk13xgfgD1hOqwy3C6K5gWSfP1ceHcoFrtRRqE5vKZmnpxaB82vQZFKJMg7E-iRm9eqtqN4G0ZfMutv0wP2v6SgSkuez_5tj17DHKVYF3dOZXlWfzST_VQw7JQ" http://localhost:8080/jwt/servlet
If everything went right this will then respond with:
This is a protected servlet
web username: test
web user has role "architect": true
web user has role "bar": false
web user has role "kaz": false
As we can see it indeed works on Tomcat, attesting to the already quite modular nature of Jakarta EE and MicroProfile implementation components.
A fully working example is again available in the Vendor EE Samples project.
The JACC Dependency
Coming back to the missing JACC dependency which we mentioned at the start of this article: it's used by Soteria to implement the Jakarta Security SecurityContext. Since JACC is mostly about exposing something that a Servlet container does in a standard way (essentially an index of security constraints) you can't really have a standalone implementation of it. The closest would be to manually parse web.xml and manually scan for the associated security annotations, build our own index, and implement the same resolving algorithm as Servlet containers are implementing. OmniFaces takes this approach for its WebXml class, but it's obviously not ideal. The best solution would be for Tomcat to implement the Servlet extensions for JACC, for which some mild interest was shown before. Finally the upcoming public Soteria SPI could be taken advantage of to use Tomcat's native APIs directly to access its internal security constraints.
With all of this not available for Tomcat there's no alternative at the moment, so we can't use the SecurityContext. Everything else works though.
Using Jakarta Security on the Payara Platform
To use Jakarta Security on the Payara Platform, nothing special is needed. It’s just there and ready to go- just like the Servlet API is just “there”, for example.
This means any book, tutorial and example on Jakarta Security can be followed, and nothing Payara-specific has to be done by the user. The following are small examples of using Jakarta Security:
- Example apps in Soteria - https://github.com/eclipse-ee4j/soteria/tree/master/test (work on other Jakarta Security implementations too)
- Java EE 8 Samples - https://github.com/javaee-samples/javaee8-samples/tree/master/security
As we've seen as a proof of concept, Jakarta Security indeed works on Tomcat, as do libraries building on top of Jakarta Security. Compared to Payara Server which offers everything out of the box, we do have some manual setup and patching to do if using Jakarta Security on Tomcat, and unfortunately not everything works at the moment. Additionally, using this manual approach with Tomcat, the burden of keeping everything updated is on the developers, and in case of bugs we have to contact multiple parties - while using Jakarta Security with the Payara Platform is a simple, one stop shop process, with the option of buying support. Payara Enterprise support customers can report bugs which will then be fixed with priority by Payara, independent of whether the root cause is within Soteria, MP JWT, or the underlying infrastructure in the Payara Platform.
Nevertheless, for people already accustomed to Tomcat or unable to migrate, experimenting a little with Jakarta Security as outlined in this article might be an interesting option.
Learn More About Payara Enterprise Support: