8

We'd like to secure our rest api using an api key. Here are the requirements:

  1. Public-facing services require an api key.
  2. "Private" services can only accept a call from within the cluster, not the outside world.
  3. Each api identifies a user, and the User object must be available to the rest service.

Is there some standard way to do this in a JAX-RS app? (We're using Resteasy.)

I've read all about filters, interceptors and basic auth, but it isn't clear to me what's the best approach.

In an earlier version of the app we had a roll-your-own solution in which public services ran on a public port and private ones on a private port. There was a custom api key lookup that set the User object as a variable into the rest service object.

I can't figure out how to do either of these things using standard JAX-RS.

ccleve
  • 15,239
  • 27
  • 91
  • 157

2 Answers2

12

Using a filter to intercept the request

This kind of authentication could be achieved with a ContainerRequestFilter, intercepting the requests to your resource methods.

The filter will be used to extract the API key from the request and validate it. If the API key is not valid, the request will be refused. Otherwise, the request will proceed to the resource methods.

Have a look at the following piece of code. The ContainerRequestContext API can be used to extract information from the HTTP request:

@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        // Extract and validate the API key from the request
        String apiKey = requestContext.getHeaderString("API-Key");
        ...
    }
}

Also have a look at this answer I wrote a while ago about authentication with tokens in JAX-RS. There you will find plenty of details that can be useful to address the situation you described in your question.

Identifying the user

During the authentication process, you must be able to identify the user who is performing the request. To propagate this information to your resource classes/methods you could:

  1. Override the SecurityContext and inject it into your resource classes/methods.
  2. Use a CDI Event and a producer method to create an object that contains the user identifier that can be injected in your resource classes/methods.

For more details on the these approaches, refer to the answer I mentioned above.

Binding the filter to some resource classes/methods

By default, the filters are global (it means they are executed for all the resource methods of your application). To bind the filter to a subset of resource methods or classes, you could use name binding annotations.

Community
  • 1
  • 1
cassiomolin
  • 124,154
  • 35
  • 280
  • 359
  • Using `X-` is discourged these days. Just go with `API-Key`. – Michael-O Oct 25 '16 at 14:19
  • @Michael-O Thanks. It was just an example on how to use the `ContainerRequestContext` API to extract a header from the HTTP request. Actually, custom headers should be avoided at all. The best approach would be using the standard `Authorization` header. Unfortunately I don't know much about the OP requirements, but I assumed the API key is being sent in a custom header. – cassiomolin Oct 25 '16 at 14:26
-2

Not giving a detailed answer, but just a suggestion. Check for CustomInvokers and register the invoker for the services. Validate the api-key and throw an error if it's not valid. If there is an error then your client gets an error. The Service code won't be called.

For the actual security framework, please check netflix zuul.

Abinash
  • 276
  • 1
  • 6