I have implemented REST service using Jersey. To give more security, I have added jersey security annotation into REST method(@PermitAll
, @DenyAll
)
Since you are performing authentication and/or authorization, instead of servlet filters I would recommend using name binding filters, so you can easily bind them to the resources you need.
To bind filters to your REST endpoints, JAX-RS provides the meta-annotation @NameBinding and can be used as following:
@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured { }
The @Secured
annotation will be used to decorate a filter class, which implements ContainerRequestFilter, allowing you to handle the request.
The ContainerRequestContext helps you to extract information from the HTTP request (for more details, have a look at the ContainerRequestContext API):
@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class SecurityFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Use the ContainerRequestContext to extract information from the HTTP request
// Information such as the URI, headers and HTTP entity are available
}
}
The ContainerRequestFilter#filter() method is a good place to abort the request if the user is not authenticated/authorized. To do it, you can use ContainerRequestContext#abortWith() or throw an exception.
The @Provider annotation marks an implementation of an extension interface that should be discoverable by JAX-RS runtime during a provider scanning phase.
To bind the filter to your endpoints methods or classes, annotate them with the @Secured
annotation created above. For the methods and/or classes which are annotated, the filter will be executed.
@Path("/")
public class MyEndpoint {
@GET
@Path("{id}")
@Produces("application/json")
public Response myUnsecuredMethod(@PathParam("id") Long id) {
// This method is not annotated with @Secured
// The security filter won't be executed before invoking this method
...
}
@DELETE
@Secured
@Path("{id}")
@Produces("application/json")
public Response mySecuredMethod(@PathParam("id") Long id) {
// This method is annotated with @Secured
// The security filter will be executed before invoking this method
...
}
}
In the example above, the security filter will be executed only for mySecuredMethod(Long)
because it's annotated with @Secured
.
You can have as many filters as you need for your REST endpoints. To ensure the execution order of the filters, annotate them with @Priority.
It's highly recommended to use one of the values defined in the Priorities class (the following order will be used):
If your filter is not annotated with @Priority, the filter will be executed with the USER priority.
You can combine this approach with Jersey security mechanism.
Additionally, you can inject ResourceInfo in your ContainerRequestFilter:
@Context
private ResourceInfo resourceInfo;
It can be used to get Method and Class which match with the requested URL:
Class<?> resourceClass = resourceInfo.getResourceClass();
Method resourceMethod = resourceInfo.getResourceMethod();
And extract the annotations from them:
Annotation[] annotations = resourceClass.getDeclaredAnnotations();
PermitAll annotation = resourceMethod.getAnnotation(PermitAll.class);