Janus Varmarken Janus Varmarken - 2 months ago 24
Java Question

JAX-RS: Custom SecurityContext has unexpected type when injected into resource method

I have implemented a

ContainerRequestFilter
that performs JWT-based authentication:

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

@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
AuthenticationResult authResult = ...
if (authResult.isSuccessful()) {
// Client successfully authenticated.
// Now update the security context to be the augmented security context that contains information read from the JWT.
requestContext.setSecurityContext(new JwtSecurityContect(...));
} else {
// Client provided no or an invalid authentication token.
// Deny request by sending a 401 response.
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
}
}
}


As you can see, I update the
SecurityContext
of the request, setting it to be an instance of my own custom implementation (
JwtSecurityContext
) if authentication succeeds. This implementation adds extra authentication and authorization data, which I would like to later access in my subsequent filter(s) and my resource methods.

I have also implemented an
AuthorizationFilter
that is invoked immediately after the
AuthenticationFilter
. Here, I can access the updated
JwtSecurityContext
just fine.

However, I am having problems when I try to inject the
JwtSecurityContext
into a resource (method).

I am currently using Jersey, and I've read the following in its documentation:


The
SecurityContext
can be directly retrieved from
ContainerRequestContext
via
getSecurityContext()
method. You can also
replace the default
SecurityContext
in a request context with a custom
one using the
setSecurityContext(SecurityContext)
method. If you set a
custom
SecurityContext
instance in your
ContainerRequestFilter
, this
security context instance will be used for injection into JAX-RS
resource class fields. This way you can implement a custom
authentication filter that may setup your own
SecurityContext
to be
used. To ensure the early execution of your custom authentication
request filter, set the filter priority to AUTHENTICATION using
constants from
Priorities
. An early execution of you authentication
filter will ensure that all other filters, resources, resource methods
and sub-resource locators will execute with your custom
SecurityContext
instance.


I try to inject the
JwtSecurityContext
into a resource method like so:

@Path("/somepath")
public class SomeResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<SomeItem> getItems(@Context SecurityContext securityContext) {
// securityContext is of type 'SecurityContextInjectee'
}
}


As the comment indicates, the runtime type of the
securityContext
variable becomes
SecurityContextInjectee
. From debugging, I've observed that this wraps a
ContainerRequest
which in turn wraps my
JwtSecurityContext
. However, there are no getters, and I do not want to use reflection to drill down this object hierarchy, so I don't know how to get a hold on my
JwtSecurityContext
.

I have tried changing
@Context SecurityContext securityContext
to
@Context JwtSecurityContext jwtSecurityContext
, but if I do this, the variable becomes
null
. I have also tried field injection, but this behaves the same way.

Am I heading down a wrong path? Should I not be accessing my custom
SecurityContext
in my resource method? One alternative could be to wrap all my data in the
Principal
implementation I return from
getUserPrincipal
in my
JwtSecurityContext
. I suppose the proxy (
SecurityContextInjectee
) would forward the call to its underlying
JwtSecurityContext
and hence return my
Principal
, but I am not sure, and ultimately I would prefer to use my
JwtSecurityContext
instead of wrapping these values in a
Principal
implementation.

Answer

You can inject the ContainerRequestContext (as mentioned in this post) and just get the SecurityContext from there.

public List<SomeItem> getItems(@Context ContainerRequestContext context) {
    JwtSecurityContext sec = (JwtSecurityContext)context.getSecurityContext();
}