This example application demonstrates how to perform token-based authentication using:
This example is inspired in my Stack Overflow best answer about token-based authentication in Jersey.
Note: For an implementation using Spring Security, have a look at the
jersey-jwt-springsecurity
project.
In a token-based authentication, the client exchanges hard credentials (such as username and password) for a piece of data called token. Instead of sending the hard credentials in every request, the client will send the token to the server to perform authentication and authorisation.
In a few words, an authentication scheme based on tokens follow these steps:
A token can be opaque which reveals no details other than the value itself (like a random string) or can be self-contained (like JWT, which is used in this example).
JWT stands for JSON Web Token. It's a standard method for representing claims securely between two parties, defined in the RFC 7519. JWT is a self-contained token and enables you to store a user identifier, an expiration date and whatever you want (but don't store passwords) in a payload, which is a JSON encoded as Base64. The payload can be read by the client and the integrity of the token can be easily checked by verifying its signature on the server.
To find some great resources to work with JWT, have a look at http://jwt.io.
JWT allows you to perform stateless authentication, that is, you won't need to persist JWT tokens if you don't need to track them. Although, by persisting the tokens, you will have the possibility of invalidating and revoking the access of them. To keep the track of JWT tokens, instead of persisting the whole token, you could persist the token identifier (the jti
claim) and some metadata (the user you issued the token for, the expiration date, etc) if you need.
Your application can provide some functionality to revoke the tokens, but always consider revoking the tokens when the users change their password. When persisting tokens, consider removing the old ones in order to prevent your database from growing indefinitely.
To build and run this application, follow these steps:
pom.xml
resides.mvn clean compile
.mvn package
.target
directory: cd target
jersey-jwt-1.0.jar
.java -jar jersey-jwt-1.0.jar
.http://localhost:8080/api
.When the application starts up, the database will be populated with the following users:
ID | Username | Password | Active | Roles |
---|---|---|---|---|
1 | admin | password | true | ADMIN, USER |
2 | user | password | true | USER |
3 | disabled | password | false | USER |
This application is packed as an uber-jar, making it easy to run, so you don't need to be bothered by installing a servlet container such as Tomcat and then deploy the application on it. Just execute java -jar <jar-file>
and the application will be up and running.
This application uses Undertow, a lighweight Servlet container designed to be fully embeddable. It's used as the default web server in the WildFly Application Server.
The uber-jar is created with the Apache Maven Shade Plugin, that provides the capability to create an executable jar including its dependencies.
Find below a quick description of the most relevant classes of this application:
AuthenticationResource
: REST endpoint for exchanging hard credentials for a JWT token.
AuthenticationFilter
: ContainerRequestFilter
implementation for extracting the authentication token from the Authorization
header of the HTTP request.
AuthorizationFilter
: ContainerRequestFilter
implementation for performing role-based authorization using Java Security annotations such as @RolesAllowed
, @PermitAll
and @DenyAll
. This implementation uses the Jersey's RolesAllowedDynamicFeature
as reference and should work with other JAX-RS implementations.
TokenBasedSecurityContext
: SecurityContext
implementation for token-based authentication.
AuthenticatedUserDetails
: Principal
implementation that holds details about the authenticated user. Used in the TokenBasedSecurityContext
.
See the curl scripts below with the REST API supported operations:
Valid credentials must be sent in the request payload to be exchanged for a token.
curl -X POST \
'http://localhost:8080/api/auth' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"username": "<username>",
"password": "<password>"
}'
No authentication is required to perform this operation.
curl -X GET \
'http://localhost:8080/api/greetings/public' \
-H 'Accept: text/plain'
Authentication and USER
role are required to perform this operation.
curl -X GET \
'http://localhost:8080/api/greetings/protected' \
-H 'Accept: text/plain' \
-H 'Authorization: Bearer <authentication-token>'
Authentication and ADMIN
role are required to perform this operation.
curl -X GET \
'http://localhost:8080/api/users' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <authentication-token>'
Authentication and ADMIN
role are required to perform this operation.
curl -X GET \
'http://localhost:8080/api/users/<user-id>' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <authentication-token>'
No authentication is required to perform this operation. However, if the request is performed with a valid token, the server will return details for the current user.
curl -X GET \
'http://localhost:8080/api/users/me' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <authentication-token>'
Alternatively to curl, you can use Postman to target the REST API. The Postman collection files are available in the src/main/postman
directory.