HiveBrain v1.2.0
Get Started
← Back to all entries
snippetjavaCritical

How to implement REST token-based authentication with JAX-RS and Jersey

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
authenticationjaxwithhowandjerseyresttokenimplementbased

Problem

I'm looking for a way to enable token-based authentication in Jersey. I am trying not to use any particular framework. Is that possible?

My plan is: A user signs up for my web service, my web service generates a token, sends it to the client, and the client will retain it. Then the client, for each request, will send the token instead of username and password.

I was thinking of using a custom filter for each request and @PreAuthorize("hasRole('ROLE')"), but I just thought that this causes a lot of requests to the database to check if the token is valid.

Or not create filter and in each request put a param token? So that each API first checks the token and after executes something to retrieve resource.

Solution

How token-based authentication works

In token-based authentication, the client exchanges hard credentials (such as username and password) for a piece of data called token. For each request, instead of sending the hard credentials, the client will send the token to the server to perform authentication and then authorization.

In a few words, an authentication scheme based on tokens follow these steps:

  • The client sends their credentials (username and password) to the server.



  • The server authenticates the credentials and, if they are valid, generate a token for the user.



  • The server stores the previously generated token in some storage along with the user identifier and an expiration date.



  • The server sends the generated token to the client.



  • The client sends the token to the server in each request.



  • The server, in each request, extracts the token from the incoming request. With the token, the server looks up the user details to perform authentication.



  • If the token is valid, the server accepts the request.



  • If the token is invalid, the server refuses the request.



  • Once the authentication has been performed, the server performs authorization.



  • The server can provide an endpoint to refresh tokens.



What you can do with JAX-RS 2.0 (Jersey, RESTEasy and Apache CXF)

This solution uses only the JAX-RS 2.0 API, avoiding any vendor specific solution. So, it should work with JAX-RS 2.0 implementations, such as Jersey, RESTEasy and Apache CXF.

It is worthwhile to mention that if you are using token-based authentication, you are not relying on the standard Java EE web application security mechanisms offered by the servlet container and configurable via application's web.xml descriptor. It's a custom authentication.
Authenticating a user with their username and password and issuing a token

Create a JAX-RS resource method which receives and validates the credentials (username and password) and issue a token for the user:
@Path("/authentication")
public class AuthenticationEndpoint {

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response authenticateUser(@FormParam("username") String username,
@FormParam("password") String password) {

try {

// Authenticate the user using the credentials provided
authenticate(username, password);

// Issue a token for the user
String token = issueToken(username);

// Return the token on the response
return Response.ok(token).build();

} catch (Exception e) {
return Response.status(Response.Status.FORBIDDEN).build();
}
}

private void authenticate(String username, String password) throws Exception {
// Authenticate against a database, LDAP, file or whatever
// Throw an Exception if the credentials are invalid
}

private String issueToken(String username) {
// Issue a token (can be a random String persisted to a database or a JWT token)
// The issued token must be associated to a user
// Return the issued token
}
}


If any exceptions are thrown when validating the credentials, a response with the status 403 (Forbidden) will be returned.

If the credentials are successfully validated, a response with the status 200 (OK) will be returned and the issued token will be sent to the client in the response payload. The client must send the token to the server in every request.

When consuming application/x-www-form-urlencoded, the client must send the credentials in the following format in the request payload:
username=admin&password=123456


Instead of form params, it's possible to wrap the username and the password into a class:
public class Credentials implements Serializable {

private String username;
private String password;

// Getters and setters omitted
}


And then consume it as JSON:
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response authenticateUser(Credentials credentials) {

String username = credentials.getUsername();
String password = credentials.getPassword();

// Authenticate the user, issue a token and return a response
}


Using this approach, the client must to send the credentials in the following format in the payload of the request:
{
"username": "admin",
"password": "123456"
}

Extracting the token from the request and validating it

The client should send the token in the standard HTTP Authorization header of the request. For example:
Authorization: Bearer


The name of the standard HTTP header is unfortunate because it carries authentication information, not authorization. However, it's the standard HTTP header for sending credentials to the server.

JAX-RS provides @NameBinding, a meta-annotation used to create other annotations to bind filters and interceptors

Context

Stack Overflow Q#26777083, score: 1563

Revisions (0)

No revisions yet.