Spring MVC Get POST body before controllers - java

I need to read POST requests body without consuming it.
I've tried it in a several ways in my custom HandlerInterceptor and nothing works. All I've tried to do to wrap and cache request bodies, at a some point, didn't work, probably even because I cannot control what Spring does.
So I thinked another thing: at some point Spring will try to map request body to an object and pass it to the right Controller. I can intercept this action and get that object before the Controller is called?

Once the request body is read from the request before reaching controller you can not use anywhere. So what you can do is to read the body from HttpServletRequest in the Filter implementation and set it in ThreadLocal variable. Then you can read from that ThreadLocal. Below is the sample code.
Inside Filter implementation class
try {
YourModel yourModel = // Read the request body
RequestContextHolder.setContext(yourModel);
}finally {
RequestContextHolder.clearContext();
}
and at any point of time in the current request you can do like below.
RequestContextHolder.getContext();
Here is the RequestContextHolder class
public class RequestContextHolder {
private static final ThreadLocal<YourModel> THREAD_LOCAL = new ThreadLocal<>();
public static void setContext(YourModel yourModel) {
THREAD_LOCAL.set(yourModel);
}
public static YourModel getContext() {
return THREAD_LOCAL.get();
}
public static void clearContext() {
THREAD_LOCAL.remove();
}
}

I've found a solution: using a CommonsRequestLoggingFilter Spring can log even the payload of the requests. The only bother I'm finding is that I can read the payload only in afterRequest method because in the beforeRequest is absent.
More info on CommonsRequestLoggingFilter here.

Related

Java Rest how to make an overall get filter

I am writing a REST service using jersey 2 and servlet 3. I have custom GET methods, all of which first look if the request has certain headers. If headers are not present, I am throwing an exception. Is there a way to provide a "parent-like" #GET method, which would reject requests withouth certain headers before they proceed to a corresponding #Path-link? Like, if my service has a name myService, and the #Path is "getHello", how to check for the headers first before going to myService/getHello annotated method?
You can use a ContainerRequestFilter and check the method
#Provider
public class CheckHeaderFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext context) {
if (context.getMethod().toUpperCase().equals("GET")) {
String header = context.getHeaderString("SomeHeader");
MultivaluedMap<String, String> headers = context.getHeaders();
if(notValidHEaders) {
context.abortWith(Response.status(400).entity("Bad").build());
// or throw WebApplicationException
}
}
}
}
If you are using package scanning to register resources, the filter should also get picked up and registered because of the #Provider annotation. Otherwise, you will need to register it yourself
See also:
Filters and Interceptors

jersey requestdispatcher execution order

I am trying to implement a RequestDispatcher using dropwizard that is supposed to look at Entity in body on posts and calculate certain stats.
so, i implemented a ResourceMethodDispatchAdapter and ResourceMethodDispatchProvider and I am able to successfully inject and invoke my RequestDispatcher,
private static class InspectRequestDispatcher implements RequestDispatcher {
private final RequestDispatcher dispatcher;
private InspectRequestDispatcher(RequestDispatcher dispatcher) {
this.dispatcher = dispatcher;
}
#Override
public void dispatch(final Object resource, final HttpContext context) {
final Saying day = context.getRequest().getEntity(Saying.class);
dispatcher.dispatch(resource, context); // this throws ConstraintViolationException
}
}
The above code throws exception, since i have already read the body ( which is understandable ), I could reset the stream, but then i will pay the penalty for reading the body twice.
Is it possible to intercept method invocation AFTER parameters have been injected ? somehow schedule this interceptor to be the last one ?
using dropwizard 7 version
If you were to use a ContainerRequestFilter instead of a RequestDispatcher, you could make use of the CachedEntityContainerRequest that's meant for exactly this.
A cached entity in-bound HTTP request that caches the entity instance obtained from the adapted container request.
A filter may utilize this class if it requires an entity of a specific type and that same type will also be utilized by a resource method.
You'd basically use it like so:
#Provider
public class StatsFilter implements ContainerRequestFilter {
#Override
public ContainerRequest filter(ContainerRequest request) {
final CachedEntityContainerRequest cachedRequest
= new CachedEntityContainerRequest(request);
final Saying saying = cachedRequest.getEntity(Saying.class);
return cachedRequest;
}
}
Then just register the filter.

Jersey - Inject variable from filter as RequestScoped

I want to perform authentication in a filter before my resource method is called. Within this filter I would also like to retrieve the permissions of a user and pass it on through a RequestScoped #Inject annotation.
#Authenticated
public class AuthenticationFilter implements ContainerRequestFilter {
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
public #interface Authenticated {};
#Inject
private ISecurityHandler handler;
public AuthenticationFilter() {}
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Filter out unauthorized
// Retrieve user permissions
this.handler.setUserPermissions(...);
}
}
Resource:
#Path("my-path")
public class GetVisitorsDataResource {
#Inject private ISecurityHandler handler;
#GET
#Path("resource-method")
#Authenticated
#Produces(MediaType.APPLICATION_JSON)
public Response resource() {
System.out.println(handler.getUserPermissions());
return Response.ok().build();
}
}
I have registered the filter and a Factory for the injection.
public static class SecurityHandlerProvider implements Factory<ISecurityHandler> {
#Override
public ISecurityHandler provide() {
System.out.println("PROVIDING SECURITY CONTEXT!");
return new SecurityHandlerImpl();
}
#Override
public void dispose(ISecurityHandler instance) {
System.out.println("DISPOSING SECURITY CONTEXT!");
}
}
I have also bound it.
bindFactory(SecurityHandlerProvider.class).to(ISecurityHandler.class).in(RequestScoped.class);
It is important that the object is created when a request is received and only accessible within that request. When the request is finished, the dispose method should be called. The only way I can achieve something similar is through the #Singleton annotation. However, the object is not destroyed after the request is completed and is shared across all requests.
I have been investing too much time into this issue already, is there perhaps anybody that knows how to achieve the preferred result?
Your code doesn't really make much sense. One place you are injecting ISecurityHandler, and another place SecurityHandler, but the factory is for ISecurityContext. I will just assume those are typos or copy and paste errors.
Aside from that I'll assume that really all is ok, since you you said it works as a singleton. So I'm guessing you are facing the "Not inside a request scope" error. The easiest fix for that is to just inject using javax.inject.Provider, which allows us to lazily retrieve the object. When the object is retrieve, it will be withing a request scope.
#Inject
private javax.inject.Provider<ISecurityContext> securityContextProvider;
#Override
public void filter(ContainerRequestContext context) throws IOException {
ISecurityContext sc = securityContextProvider.get();
}
...
bindFactory(SecurityHandlerProvider.class)
.to(ISecurityContext.class)
.in(RequestScoped.class);
NB, you should also make sure to annotate you AuthenticationFilter with #Priority(Priorities.AUTHENTICATION) so that it occurs before any other filter even you my prefer it to be a #PreMatching filter. The earlier into the system the authentication happens, the better, I'd say.
As an aside, you may want to look into Jersey's RolesAllowedDynamicFeature. It allows you to use the jsr250 annotations #RolesAllowed, #DenyAll, and #PermitAll for your resource classes and methods.
It is basically a filter that occurs after your Priorites.AUTHENTICATION filter, and it looks up the javax.ws.rs.core.SecurityContext from the ContainerRequestContext to look up roles. You just need to create the SecurityContext inside your authentication filter, so the next filter can look it up.
You can see an example here. You can check the user permission in the isUserInRole. When the set the SecurityContext, Jersey's filter will be called afterwards, and it calls your isUserInRole. Doing it this way, you get access control for free.

Injecting principal into resource method in RESTEasy with Guice

I am developing a REST API using RESTEasy with Guice and at the moment I am trying to incorporate basic authentication by use of an annotation similar to the #Auth found in Dropwizard. With
#Path("hello")
public class HelloResource {
#GET
#Produces("application/json")
public String hello(#Auth final Principal principal) {
return principal.getUsername();
}
}
the hello resource invocation should be intercepted by some code performing basic authentication using the credentials passed in the Authorization HTTP request header and on success injecting the principal into the method principal parameter. I would also like to be able to pass a list of allowed roles to the annotation, e.g. #Auth("admin").
I really need some advice in what direction to go to achieve this?
I think your best bet would be using an intermediate value within request scope. Assuming that you didn't put HelloResource in singleton scope, you can inject this intermediate value in some ContainerRequestFilter implementation and in your resource, and you can fill it inside this ContainerRequestFilter implementation with all authentication and authorization info you need.
It will look something like this:
// Authentication filter contains code which performs authentication
// and possibly authorization based on the request
#Provider
public class AuthFilter implements ContainerRequestFilter {
private final AuthInfo authInfo;
#Inject
AuthFilter(AuthInfo authInfo) {
this.authInfo = authInfo;
}
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// You can check request contents here and even abort the request completely
// Fill authInfo with the data you need
Principal principal = ...; // Ask some other service possibly
authInfo.setPrincipal(principal);
}
}
#Path("hello")
public class HelloResource {
private final AuthInfo authInfo;
#Inject
HelloResource(AuthInfo authInfo) {
this.authInfo = authInfo;
}
#GET
#Produces("application/json")
public String hello() {
// authInfo here will be pre-filled with the principal, assuming
// you didn't abort the request in the filter
return authInfo.getPrincipal().getUsername();
}
}
public class MainModule extends AbstractModule {
#Override
protected void configure() {
bind(AuthFilter.class);
bind(HelloResource.class);
bind(AuthInfo.class).in(RequestScoped.class);
}
}
And even if you did put the resource (or even the filter) in singleton scope for some reason, you can always inject Provider<AuthInfo> instead of AuthInfo.
Update
It seems that I was somewhat wrong in that the filter is by default not in singleton scope. In fact it seem to behave like singleton even though it is not bound as such. It is created upon JAX-RS container startup. Hence you will need to inject Provider<AuthInfo> into the filter. In fact, the container startup will fail if AuthInfo is injected into the filter directly while being bound to request scope. Resource (if not explicitly bound as singleton) will be OK with direct injection though.
I have uploaded working program to github.

Change the body of a request

I have a Java Servlet, which is handling a REST request. However, this is breaking when it receives invalid data. This is POSTed in XML format, and to attempt to resolve this I have added a filter into the filter chain. The filter is being called, and I can access the data in the body of the request, accessing the XML.
I can validate this and manipulate it to ensure that the data is correct, but I cannot work out how to reset it back into the request object.
How can you set the body of an HttpServletRequest object?
You can wrap Your HttpServletRequest object with a new class lets name it: NewHttpServletRequest. The actual rewriting should be done in the appropriate overriden methods e.g getParameter(String)
package com.example;
import javax.servlet.http.HttpServletRequestWrapper;
public class MyHttpServletRequest extends HttpServletRequestWrapper {
public MyHttpServletRequest(HttpServletRequest request) {
super(request);
}
public String getParameter(String name) {
String str = super.getParameter(name);
// DO THE REWRITING
return str;
}
}
Take look at HttpServletRequestWrapper
You can wrap original request with a new object by using public HttpServletRequestWrapper(HttpServletRequest request) constructor, you won't have to do a lot work yourself.

Categories

Resources