This guide shows you how to secure a rest endpoint using Jakarta Authentication.
As we delve into securing RESTful web services, let’s first define a basic scenario that we will be working with throughout this article.
We are constructing a service that will accept an HTTP GET request at http://localhost:8080/jakartaee-hello-world/rest/hello. In response to this request, our service will return a JSON payload, as shown in the following example:
{
"hello": "world"
}
In this article, we will secure this endpoint.
Over time, this service can be further enhanced and customized to fit more complex requirements and situations. By starting with this simple setup, we can get a better grasp of the fundamental steps involved in securing a RESTful web service using Jakarta EE. Now, let’s proceed with those steps. For those unfamiliar with RESTful web services, we recommend reading our previous article.
To install JDK and Maven, we can use the SDKMan. We can go through the steps mentioned in the how-to guide.
In this getting started guide, you may use Eclipse Starter for Jakarta EE, finish each step, or skip fundamental setup stages you already know. You can also begin with an IDE or choose a project structure from well-known Maven archetypes.
To use Eclipse Starter for Jakarta EE, we need to take the following steps:
When we unpack the generated code, we will have a structure of an application. We can open it in our favourite IDE, and then we can just run it from the command line.
.
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
└── main
├── java
│ └── org
│ └── eclipse
│ └── jakarta
│ └── hello
│ ├── Hello.java
│ └── HelloWorldResource.java
└── webapp
├── WEB-INF
│ └── web.xml
├── images
│ └── jakartaee_logo.jpg
└── index.html
This generated source code comes with an embedded Maven wrapper. So if you want to use it, make sure you run the following command first for Unix environments (Linux or Mac):
$ chmod +x mvnw
Since we are using WildFly as a runtime, the following command would run the application:
$ ./mvnw clean package wildfly:run
On the other hand, if you have Maven installed, you can run the following command:
$ mvn clean package wildfly:run
For Windows, we don’t need to run chmod
command; rather use mvnw.cmd
instead of mvnw
.
Now, if you hit the browser with the following URL, you will see the result.
http://localhost:8080/jakartaee-hello-world
We have already covered how to test them out in our previous article, so we’re skipping it.
The first step in securing your RESTful web service is to authenticate the user making the request. In Jakarta EE, this can be achieved using Jakarta Authentication Specification. This Specification can be used to set up various forms of authentication, including basic, form-based, digest, and token-based authentication.
Let’s consider basic authentication. To set this up, we’d need to configure your web.xml file with the following:
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected REST Services</web-resource-name>
<url-pattern>/rest/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>ApplicationRealm</realm-name>
</login-config>
<security-role>
<role-name>user</role-name>
</security-role>
It sets up basic authentication for the application and protects resources
under the /rest
path.
Let’s break it down:
<web-resource-collection>
tag is used to
group URLs to apply security constraints. In this configuration, the web
resource collection is named ‘Protected REST Services’, and it applies to
all URLs under the /rest
path, as defined by
<url-pattern>/rest/*</url-pattern>
.<auth-constraint>
tag is used to define the
roles that are allowed to access the URLs defined in the web resource
collection. Here, only users with the 'user'
role are allowed access.<realm-name>
is set to
ApplicationRealm
, which is the realm used by the server for authentication.<security-role>
tag is used to define roles
that can be used in the web application. In this case, the role of ‘user’ is
defined. Note that the names used in the <role-name>
tags need to match the
roles defined in the server or in your application’s security setup.Once authentication is successful, the next step is authorization. Authorization is the process of determining whether the authenticated entity has the right to access the desired resources or perform a certain action.
Jakarta EE uses role-based access control (RBAC) for authorization. In RBAC, access decisions are based on the roles that individual users have as part of the system. An entity’s roles are verified against the security constraints defined in the deployment descriptor of the application.
Jakarta EE provides several annotations to secure your applications:
@RolesAllowed:
indicates which security roles are allowed to invoke the
specified methods.@DenyAll:
specifies that no security roles are allowed to invoke the
specified methods.@PermitAll:
specifies that all security roles are allowed to invoke the
specified methods.@RunAs:
defines the security identity for executing the specified methods.Now let’s secure our endpoint. To do that, we have just to make the following
change in the HelloWorldResource
:
@Path("hello")
public class HelloWorldResource {
@GET
@RolesAllowed("user")
@Produces({ MediaType.APPLICATION_JSON })
public Hello hello(@QueryParam("name") String name) {
if ((name == null) || name.trim().isEmpty()) {
name = "world";
}
return new Hello(name);
}
}
In this article, we are using @RolesAllowed
annotation to secure this
endpoint.
If we run the application, our endpoint will be secured. Let’s try it out.
$ curl -v http://localhost:8080/jakartaee-hello-world/rest/hello
This will produce the following results:
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /jakartaee-hello-world/rest/hello HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.87.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 401 Unauthorized
< Expires: 0
< Connection: keep-alive
< WWW-Authenticate: Basic realm="ApplicationRealm"
< Cache-Control: no-cache, no-store, must-revalidate
< Pragma: no-cache
< Content-Type: text/html;charset=UTF-8
< Content-Length: 71
< Date: Wed, 05 Jul 2023 01:18:44 GMT
<
* Connection #0 to host localhost left intact
<html><head><title>Error</title></head><body>Unauthorized</body></html>
As we can see, our curl command receives a 401 Unauthorized response because it lacks valid authentication credentials for the requested resource.
The next step is to create a credential so that we can access this endpoint securely.
In WildFly, the application-users.properties
and
application-roles.properties
files are typically located in the
standalone/configuration directory of your WildFly installation. Since we are
using embedded WildFly, to find this folder, we have to navigate to
target/server/standalone/configuration
folder.
application-users.properties
is used to define users and their passwords
(hashed), while application-roles.properties
is used to map users to roles.
Here’s how to do it:
/target/server/bin
) folder.add-user.sh
(or add-user.bat
for Windows) script to create a new
user.The script will guide you through the process of adding a new user:
$ ./add-user.sh
Follow the prompts:
What type of user do you wish to add?
a) Management User (mgmt-users.properties)
b) Application User (application-users.properties)
(a): b
Enter the details of the new user to add.
Using realm 'ApplicationRealm' as discovered from the existing property files.
Username : your_username
Password : your_password
Re-enter Password : your_password
What roles do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[ ]: your_role
These are the simple steps to define users and roles in WildFly. Restart the WildFly server to ensure your changes take effect.
However, we have to be sure, this time, we don’t use clean the target folder while running from maven. Because the maven clean command removes the target folder altogether, and our user creation will be lost.
So simply run without clean
:
./mvnw package wildfly:run
Now let’s run the curl command again:
curl --user username:password http://localhost:8080/jakartaee-hello-world/rest/hello
Replace the username and password you configured on add-user.sh
prompt.
If username and password are correct, then we will get the following output.
{
"hello": "world"
}
Congratulations! You have just learned how to secure a rest endpoint using Jakarta Authentication. While this is a crucial starting point, real-world applications typically store user and role data in databases or LDAP servers rather than in properties files. Be aware that security is multifaceted and encompasses aspects such as HTTPS utilization and database protection. For a comprehensive understanding, it’s recommended to refer to the official Jakarta EE security documentation and explore other relevant resources online.