Preloader image

TomEE has its own independent Jakarta Security implementation https://eclipse-ee4j.github.io/security-api/ .

Jakarta Security defines a standard for creating secure Jakarta EE applications in modern application paradigms. It defines an overarching (end-user targeted) Security API for Jakarta EE Applications.

Jakarta Security builds on the lower level Security SPIs defined by Jakarta Authentication and Jakarta Authorization, which are both not end-end targeted.

As the specification requires, TomEE supports by default JDBC and JDAP identity stores. It also has a default support for Tomcat’s 'tomcat-users.xml' (See security-tomcat-user-identitystore example).

This example will show how you can leverage your own identity store to authenticate users. This is very often required for integrating your systems.

Implement a simple servlet

This movie servlet, is a very simple example that defines a BasicAuthenticationMechanism, some roles and security constraints.

@WebServlet("/movies")
@DeclareRoles({"foo","bar","kaz"})
@ServletSecurity(@HttpConstraint(rolesAllowed = "foo"))
@BasicAuthenticationMechanismDefinition
public class MovieServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Override
    public void doGet(final HttpServletRequest request, final HttpServletResponse response)
        throws ServletException, IOException {

        String webName = null;
        if (request.getUserPrincipal() != null) {
            webName = request.getUserPrincipal().getName();
        }

        response.getWriter().write(
            "<html><body> Welcome to Movie servlet <br><br>\n" +

            "web username: " + webName + "<br><br>\n" +

            "web user has role \"foo\": " + request.isUserInRole("foo") + "<br>\n" +
            "web user has role \"bar\": " + request.isUserInRole("bar") + "<br>\n" +
            "web user has role \"kaz\": " + request.isUserInRole("kaz") + "<br><br>");
    }

}

IMPORTANT:

In TomEE, Jakarta Security is wired in all layers, you can use

  • jakarta.ws.rs.core.SecurityContext#getUserPrincipal and isUserInRole to get the User Principal and check if the user has a given role

  • jakarta.security.enterprise.SecurityContext#getCallerPrincipal and isCallerInRole to get the Caller Principal (notice the difference in terms of naming) and check if a caller has a given role

  • jakarta.servlet.http.HttpServletRequest#getUserPrincipal and isUserInRole

  • jakarta.ejb.SessionContext#getCallerPrincipal and isCallerInRole

  • the Subject from the PolicyContext but this is less used

A lot of different APIs to retrieve the principal and check whereas it has a given role. It’s all wired in and consistent in TomEE. No special configuration is needed.

Create your own IdentityStore implementation

For the sake of keeping this example as simple as possible, the TestIdentityStore is very simple.

It recognizes only 2 users and only one of them has the right roles to call the servlet.

@ApplicationScoped
public class TestIdentityStore implements IdentityStore {

    public CredentialValidationResult validate(Credential credential) {

        if (!(credential instanceof UsernamePasswordCredential)) {
            return INVALID_RESULT;
        }

        final UsernamePasswordCredential usernamePasswordCredential = (UsernamePasswordCredential) credential;
        if (usernamePasswordCredential.compareTo("jon", "doe")) {
            return new CredentialValidationResult("jon", new HashSet<>(asList("foo", "bar")));
        }

        if (usernamePasswordCredential.compareTo("iron", "man")) {
            return new CredentialValidationResult("iron", new HashSet<>(Collections.singletonList("avengers")));
        }

        return INVALID_RESULT;
    }

}

There is nothing else to configure or do. The identity store must implement the IdentityStore interface. It must be a CDI bean and then TomEE will pick it up automatically and delegate user authentication.

Running

Were we to run the above Main class or Test Case we’d see output like the following:

....
INFOS: Starting ProtocolHandler ["http-nio-54313"]
juin 24, 2021 2:58:42 PM sun.reflect.DelegatingMethodAccessorImpl invoke
INFOS: Server startup in [4703] milliseconds
juin 24, 2021 2:58:42 PM sun.reflect.DelegatingMethodAccessorImpl invoke
INFOS: Full bootstrap in [7638] milliseconds


Calling MovieServlet without any credentials provided.
juin 24, 2021 2:58:43 PM com.gargoylesoftware.htmlunit.WebClient printContentIfNecessary
INFOS: statusCode=[401] contentType=[text/html]
juin 24, 2021 2:58:43 PM com.gargoylesoftware.htmlunit.WebClient printContentIfNecessary
INFOS: <!doctype html><html lang="en"><head><title>HTTP Status 401 – Unauthorized</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 401 – Unauthorized</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The request has not been applied because it lacks valid authentication credentials for the target resource.</p><hr class="line" /><h3>Apache Tomcat (TomEE)/9.0.52 (10.0.0-M1-SNAPSHOT)</h3></body></html>


Calling MovieServlet with a valid user and valid permissions.


Calling MovieServlet with the wrong credentials.
juin 24, 2021 2:58:44 PM com.gargoylesoftware.htmlunit.WebClient printContentIfNecessary
INFOS: statusCode=[401] contentType=[text/html]
juin 24, 2021 2:58:44 PM com.gargoylesoftware.htmlunit.WebClient printContentIfNecessary
INFOS: <!doctype html><html lang="en"><head><title>HTTP Status 401 – Unauthorized</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 401 – Unauthorized</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The request has not been applied because it lacks valid authentication credentials for the target resource.</p><hr class="line" /><h3>Apache Tomcat (TomEE)/9.0.52 (10.0.0-M1-SNAPSHOT)</h3></body></html>


Calling MovieServlet with a valid user but without required permissions.
juin 24, 2021 2:58:44 PM com.gargoylesoftware.htmlunit.WebClient printContentIfNecessary
INFOS: statusCode=[403] contentType=[text/html]
juin 24, 2021 2:58:44 PM com.gargoylesoftware.htmlunit.WebClient printContentIfNecessary
INFOS: <!doctype html><html lang="en"><head><title>HTTP Status 403 – Forbidden</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 403 – Forbidden</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Message</b> Access to the requested resource has been denied</p><p><b>Description</b> The server understood the request but refuses to authorize it.</p><hr class="line" /><h3>Apache Tomcat (TomEE)/9.0.52 (10.0.0-M1-SNAPSHOT)</h3></body></html>