Reputation: 4801
I have implemented REST service using Jersey. To give more security, I have added jersey security annotation into REST method(@PermitAll
, @DenyAll
).
Below is my sample REST service:
@GET
@Path("/getall")
@Produces(MediaType.APPLICATION_JSON)
@PermitAll
public String getChartSupportedData(@QueryParam("items") int result) {
// my code goes here
}
But the problem is that previously I have used javax.servlet.Filter
filter to validate URI.
web.xml:
<filter>
<filter-name>ApplicationFilter</filter-name>
<filter-class>web.filter.ApplicationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ApplicationFilter</filter-name>
<url-pattern>/rest/api/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
According to access some REST services, HttpServletRequest
should contain a valid token (generated by the application).
Some REST end points doesn't require a token to access the service. In that case, I have to bypass that in filter implementation:
private static String[] bypassPaths = { "/data/getall" };
So my requirement is something like that.
If we declared some REST end point as @PermitAll
that path should not have declare in filter as bypass path so that anyone can access it without valid token.
But the problem is that filter is always filtering when the request comes into server and, if it's not in the bypass array the request doesn't continue even I declared as @PermitAll
.
I would like to know if can I combine those two security options in same web application.
Upvotes: 2
Views: 5551
Reputation: 130867
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);
Upvotes: 10