Zero Trust Security in Enterprise Java: What it is and How to Implement it
Cybersecurity isn’t just about building walls, fortresses, moats or any other external barrier anymore. Nowadays, it’s important to check […]
If you’re building a REST service, then that REST service will expose some kind of data or will allow some kind of interactions with a server. For instance, consider a Facebook REST service that allows you to retrieve your chat history. Naturally you don’t want just anyone looking at that history, hence the need for security.
For Java EE security there are a few basic concepts involved:
For Java EE Security / the Payara Platform, there are a couple of options:
The Payara Platform ships with the authentication mechanisms defined by the Servlet-, Java EE Security-, MicroProfile- and Payara APIs. These all can be used largely interchangeably. Note that not all of them are primarily useful for REST authentication, but are instead more suited for interactive websites (specifically the FORM based ones).
The Payara Platform ships with the identity stores defined by the Java EE Security API, and its own internal ones (a JAAS LoginModule/GlassFish Realm combination) that can be configured via the admin console, CLI or domain.xml. In Payara 5.182 and before identity stores from Java EE Security can be paired with authentication mechanisms from Java EE Security, MicroProfile and the Payara API, but not with those from Servlet. Likewise, the internal ones can only be paired with the authentication mechanisms from Servlet. Payara 5.182will likely make these all interchangeable. Note that MicroProfile JWT has its own identity store, but it can be paired if needed with additional identity stores if extra roles beyond those provided by the JWT token are needed.
For our first example we’ll be demonstrating Java EE Security BASIC authentication with a custom (application provided) identity store.
We’ll start with defining the REST Service (JAX-RS resource) itself as follows:
@Path("/resource") @Produces(TEXT_PLAIN) public class Resource { @GET @Path("hi") @RolesAllowed("a") public String hi() { return "hi!"; } }
In this simple example we just have a single resource method returning the text “hi!”. Special about this REST Service is the use of the @RolesAllowed annotation, which causes the resource method to be secured, and thus only accessible for callers who are associated with (are in the/have the) role “a”. Note that this annotation is supported out of the box by Payara, but may not be supported directly on other servers, or may have to be activated explicitly on those other servers.
Next we’ll create an activator class that activates JAX-RS itself, but most importantly for this example defines that we want to use the BASIC authentication mechanism from Java EE:
@ApplicationScoped @ApplicationPath("/rest") @DeclareRoles({ "a", "b" }) @BasicAuthenticationMechanismDefinition(realmName = "foo-ee") public class JaxRsActivator extends Application { }
The @BasicAuthenticationMechanismDefinition annotation causes Payara to put a CDI bean implementing the BASIC authentication mechanism in service.
The last piece is the custom identity store, which contains the data for a single test caller:
@ApplicationScoped public class TestIdentityStore implements IdentityStore { public CredentialValidationResult validate(UsernamePasswordCredential usernamePasswordCredential) { if (usernamePasswordCredential.compareTo("test", "secret")) { return new CredentialValidationResult("test", new HashSet<>(asList("a", "b"))); } return INVALID_RESULT; } }
In this minimum identity store we recognise a caller with as credentials a username “test” and a password “secret”. For this caller we’ll return the same username and the groups “a” and “b” (which are mapped to roles of the same name by default, so we don’t have to worry about groups/roles now).
When we bundle this together into a war called “test” and deploy it to a default Payara Server instance, we can request http://localhost:8080/test/rest/resource/hi and the browser should respond with a dialog asking for the credentials. If we enter “test” and “secret” we’ll see “hi!” on our screen, otherwise we’ll see either a 401 or the browser will redisplay the dialog.
Demonstrating that much of our code stays the same when switching authentication mechanisms between EE Security and Servlet, we’ll also show the conceptual same BASIC mechanism configured via Servlet. For that the REST Service stays the same, but we remove the authentication mechanism annotation from the activator class:
@ApplicationScoped @ApplicationPath("/rest") @DeclareRoles({ "a", "b" }) public class JaxRsActivator extends Application { }
In its place we add a WEB-IN/web.xml file with the following contents:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <login-config> <auth-method>BASIC</auth-method> <realm-name>foo-servlet</realm-name> </login-config> </web-app>
Note that the realm name, contrary to a very persistent public belief, is not officially specified to indicate which identity store is being used. The string here (“foo-servlet”) is supposed to be only used in the header that is sent to the caller (and which for example a browser renders as a dialog box). But in Payara it can optionally indicate which Payara Realm/LoginModule combo is used. If it’s an unknown Payara Realm it will still be sent to the browser, but the default Payara realm will be used.
The next thing to do is configuring Payara’s global identity store, which in Payara terms is called a realm. The default global realm is File, and since that’s exactly the one that’s easiest for this example we don’t have to actually select it. We do have to a user called “test” to it. With Payara running, execute the following command from [payara install dir]/bin:
./asadmin create-file-user --groups a test
When asked for the password enter “secret”.
Now when deploying this again and requesting http://localhost:8080/test/rest/resource/hi once more, we should see the same result as with the first variant of this application.
The full source of both variants can be found on GitHub, along with an automated tests that demonstrates how the applications are supposed to be called:
{{cta(‘4c7626f5-1e53-418e-90c8-add6e4af19c9’)}}
Share:
Cybersecurity isn’t just about building walls, fortresses, moats or any other external barrier anymore. Nowadays, it’s important to check […]
Middleware runs quietly in the background of most applications, which makes it easy to overlook its lifecycle. In effect, […]
If your organization runs Jakarta EE applications, securing the application server they rely on is not a one-time project. […]
it works!! thanks a lot