I have a web service like this:
@Path("/projects")
public class Projects {
[...]
@Inject
CurrentRequest current;
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}")
public Response getProject(@PathParam("id") String id) {
if (current.isUserAuthenticated()) {
[...do something...]
} else {
[...produce an error...]
}
}
}
And a CDI bean with an auth checker method like this:
@RequestScoped
public class CurrentRequest {
public boolean isUserAuthenticated() {
[...do some header checking...]
}
}
My problem is that I can't for the life of me get hold of the HTTP headers from inside CurrentRequest
. I tried injecting HttpServletRequest
, but it's not initialized. I tried using @Context
, same thing. Obviously FacesContext.getCurrentInstance()
doesn't work either because there is no FacesContext.
I see that this question is basically asking the same thing, but hasn't received much attention.
My current approach is to use @Context HttpServletRequest request
inside Projects
and pass it as a parameter to current.isUserAuthenticated(request)
. But that feels so wrong. Shouldn't the CDI bean know its own request?
What am I missing?
You don't need the HttpServletRequest
in your JAX-RS endpoints to get the HTTP headers from the request. Instead, you can inject HttpHeaders
:
@Context
HttpHeaders httpHeaders;
Then you can use the HttpHeaders
API to get the header values:
HttpHeaders#getHeaderString(String)
HttpHeaders#getRequestHeaders()
HttpHeaders#getHeaderString(String)
If you need the value of a standard HTTP header, consider using the constants available in the HttpHeaders
API:
// Get the value of the Authorization header
String authorizationHeader = httpHeaders.getHeaderString(HttpHeaders.AUTHORIZATION);
Since you are performing authentication and/or authorization, I would recommend using filters, so you can keep your REST endpoints lean and focused on the business logic.
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):
AUTHENTICATION
AUTHORIZATION
ENTITY_CODER
HEADER_DECORATOR
USER
If your filter is not annotated with @Priority
, the filter will be executed with the USER
priority.
You'll probably find this answer useful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With