Accessing both Request and Response in a Jersey Filter - jersey

In Jersey, one can add a ContainerRequestFilter or a ContainerResponseFilter
public class RequestFilter implements ContainerRequestFilter {
#Override
public ContainerRequest filter(ContainerRequest containerRequest) {
// logic
}
}
public class ResponseFilter implements ContainerResponseFilter {
#Override
public ContainerResponse filter(ContainerRequest request,
ContainerResponse response) {
// logic
}
}
Both are added using PackagesResourceConfig:
PackagesResourceConfig prc = new PackagesResourceConfig("com.company.xxx");
prc.getContainerRequestFilters().add(new RequestFilter());
prc.getContainerResponseFilters().add(new ResponseFilter());
Although the response filter also has access to the request, it is called AFTER the endpoint is called. What we require is the ability to access the request and the response in the same filter BEFORE the endpoint is called. The request filter is called before the endpoint, but does not have access to the response.
There seems to be no way to add a standard javax.servlet.Filter which has access to both the HttpServletRequest and the HttpServletResponse, and is called before the endpoint is invoked.
Thanks

You cannot filter anything without calling the endpoint, because that's how the request comes to your service. If you meant without hitting the controller, that's a different story. But even then, you want something like pre-matching response filter, which naturally does not exist. It defeats the purpose of your service. Why not just add whatever header you want on all responses after the controller and any other pieces in the handling chain finish their work?! You can access both the request context and the response context in the response filter, so as Paul mentioned in the comments you could store some information you will need in the requestContext while in the ContainerRequestFilter.
JAX-RS spec has some good examples of both filters and interceptors.

Related

Using a request header value in #PreAuthorize

Is it possible to use a request header value in #PreAuthorize?
In my app, all requests have a custom header included which I need to use in conjunction with the user role to determine whether or not they should be allowed to access the controller.
It's ok if someone manually specifies a header as that won't be a security issue, as ultimately the role will control this. But I will need to use it to cut down on checking for that manually in each controller method.
Thank you,
Matt
1 - This may be the fastest method if you will only use it in a few places.
#GetMapping(value = "/private-api-method")
#PreAuthorize("#request.getHeader('header-name') == 'localhost:8080'")
public ResponseEntity<String> privateApiMethod(HttpServletRequest request) {
return ResponseEntity.ok("OK!");
}
OR
#GetMapping(value = "/private-api-method")
#PreAuthorize("#header == 'localhost:8080'")
public ResponseEntity<String> privateApiMethod(#RequestHeader("header-name") String header) {
return ResponseEntity.ok("OK!");
}
2 - This may be the best method if you will use it in many places. (In the SecurityServise, you can add multiple different methods of checking.)
#GetMapping(value = "/private-api-method")
#PreAuthorize("#securityService.checkHeader(#request)")
public ResponseEntity<String> privateApiMethod(HttpServletRequest request) {
return ResponseEntity.ok("OK!");
}
3 - You can choose a special method for yourself
A Custom Security Expression with Spring Security
Since you intend to check for a particular header/cookie/request-attribute for every controller methods, you should opt for a Filter as this would be a standard and you can have a guarantee for it be executed for each and every method and that too only once by extending from OncePerRequestFilter
Having said that, there would be 2 way you can achieve this:
By extending AbstractAuthenticationProcessingFilter or OncePerRequestFilter
For this you may refer the spring-security jwt token validation flow which all would advocate for:
Add method security at your desired controller method as #PreAuthorize("hasAuthority('USER_ROLE')")
Intercept the request before UsernamePasswordAuthenticationFilter, extract the Authentication header or cookies from the request and validate the token value for claims.
public class CustomHeaderAuthFilter extends AbstractAuthenticationProcessingFilter{
#Override
public Authentication attemptAuthentication(
HttpServletRequest request, HttpServletResponse response){
// Get all the headers from request, throw exception if your header not found
Enumeration<String> reqHeaders = request.getHeaderNames();
Assert.notNull(reqHeaders, "No headers found. Abort operation!");
Collections.list(reqHeaders)
.stream()
.filter(header_ -> header_.equals("TARGET_HEADER_NAME"))
.findAny().ifPresent(header_ -> {
// header found, would go for success-andler
});
// Here it means request has no target header
SecurityContextHolder.clearContext();
failureHandler.onAuthenticationFailure(request, response, new CustomException(""));
}
}
Going by this way, you need to register your filter with WebSecurityConfigurerAdapter and you may also provide your AuthenticationProvider if you extend from AbstractAuthenticationProcessingFilter.
By accessing HTTP Headers in rest controllers using #RequestHeader as dm-tr has mentioned.
Maybe you can try this
#PreAuthorize("hasAuthority('ROLE_SOMETHING')")
#RequestMapping("PATH")
public void checkIt(#RequestHeader("header-name") String header) {
if (null != header /* && header meets certain condition*/) {
// stuff
} else throw new ResponseStatusException(HttpStatus.FORBIDDEN); // PERMISSION NOT GRANTED, 403 ERROR
}

Resolving POST /** request URL to full request URL using micrometer

With the micro-service architecture I have written a generic POST request handler which is consumed by all the micro-services. The post mapping in spring look like this:
#RestController
#RequestMapping(value = "/v1/", consumes = {MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE})
public class V1Controller {
#PostMapping(path = "/**")
public #ResponseBody Json post () {}
}
Now while I am consuming the metrics for this endpoint using micrometer I am only getting /v1/ as the endpoint in the metrics while I am sending the full URL like /v1/demo/foo from the calling service. I tried lot of the combination but it is not working. I have also added the WebMvcTagsProvider where I am listing to request and resolving the POST api calls.
#Bean
#SuppressWarnings("unchecked")
public WebMvcTagsProvider webMvcTagsProvider(ObjectMapper objectMapper) {
return new DefaultWebMvcTagsProvider() {
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) {
if ("POST".equals(request.getMethod())) {
Tag uriTag = Tag.of("uri", String.valueOf(request.getRequestURI()));
return Tags.of(WebMvcTags.method(request), uriTag, WebMvcTags.exception(exception), WebMvcTags.status(response));
}
return Tags.of(WebMvcTags.method(request), WebMvcTags.uri(request, response), WebMvcTags.exception(exception), WebMvcTags.status(response));
}
};
}
Still it is resolving to /v1/ URL in the metrics. I tried googling alot but didn't find any solution. Thanks in advance.
The build in Spring Boot RequestMapping based metrics match on the annotations and add those as tags.
This is to avoid a tag explosion. Imagine a #RequestMapping for a path like user/{userId}, you would want to group all those calls together (user/1, user/2, user/3).
You'll want to create your own Timer in your post method that set that url tags, etc there.
If you decide to reuse the same metric name as the built in Spring Boot metric, you'll want to disable that one as well, so you don't double count those requests.

How to intercept a RequestBody before RestController and do some business rules handling from another microservice?

Basically, we have a big monolithic application built on Spring Boot v1.2 and we would like to handle some business rules processing from a MicroService (let's call it BR engine) built on Spring Boot v2.1.6.
How can I intercept the requestBody and send it first to BR engine and then once done, it will either proceed to the actual handler (Monolithic Controller) or not based from BR engine results - for simplicity let's say BR engine returns either true or false. If true, proceed to actual handler if false, return an exception.
I wanted to use HandlerInterceptorAdapter however, not sure how I can intercept the requestBody and pass it to a microservice - then from the results it will either proceed or not to the actual handler.
As an example, let's say I have a POST mapping to the Monolithic controller:
#PostMapping("/save")
public ResponseEntity<Client> save(#RequestBody ClientDTO dto) {
log.debug("Saving...");
Client newClient = Client.builder().build();
BeanUtils.copyProperties(dto, newClient);
return new ResponseEntity<>(clientService.save(newClient), HttpStatus.OK);
}
Now I wanted to intercept the ClientDTO requestBody and send it first to the BR engine and do some stuff from there. I have thought of using an interceptor and add it to my config which implements WebMvcConfigurer. However, I am not sure how can perform a restTemplate here and get a response from BR engine of pass or fail - if fail the actual handler will be skipped and just throw an exception
#Component
public class RuleEngineInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// intercept requestBody then send it to BR engine
// but how? and how can I get the response from BR engine
// to decide whether it will proceed to actual handler
// or not - probably return an exception.
return true;
}

Copy RequestParams to RequestHeaders before handling in RestController

To replace a legacy system and not breaking the interface, I'm looking for a way to implement the following scenario:
If a REST client hasn't set a specific HTTP header (applicationId) but sends it as a query-paramter (aka RequestParameter), this value should be taken as a method parameter in a Spring Boot RestController.
The current method looks like this:
#RequestMapping(value = "/something", method = RequestMethod.GET)
public void doSomething(#RequestHeader("applicationId") String applicationId) { }
I think there could be two possible ways:
Annotate the method somehow to map a query-parameter OR a header to a method parameter
Write an Interceptor which reads all query-parameters of a request and set non-existing headers with their values. This way, the method wouldn't have to be touched at all.
In both ways I'm not sure how to implement them (don't know if 1. is even possible). I tried with an own HandlerInterceptor which reads query-params in preHandle (successfully) but isn't able to set headers in the request before it is forwarded to the RestController.
Write a Filter that wraps the incoming request using a HttpServletRequestWrapper. This wrapper should override the getHeader method.
public ParameterToHeaderWrappingRequestFilter extends OncePerRequestFilter {
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
filterChain.doFilter(new ParameterToHeaderWrappingRequest(request), response, filterChain);
}
}
Register this filter as a #Bean in your Spring Boot application and it will be applied automatically.
public class ParameterToHeaderWrappingRequest extends HttpServletRequestWrapper {
public String getHeader(String name) {
String header = super.getHeader(name);
if (header == null) {
header = getParameter(name);
}
return header;
}
}
Something like that should do the trick. Depending on your needs you might want/need to override some additional header based methods and you probably want to limit the number of headers to override with parameters.
The rest of your code can now be written as is.

Building a façade with spring which calls another server and returns its response

For an application I need to create a security façade in Spring 4.x.
This thiny layer must accepts any request from our mobile application and execute a security check for the provided token (with openId and Oauth).
Upon a successful validation, the request needs to be forwarded to the backend application, which does not need to be aware of the security token mechanism.
Thus, the flow will be something like this:
security_facade_url/path/of/the/request
With a header that indicates the backend to invoke upon successful validation of the token
Upon successful validation the security façade sends a request to the backend URL
backend_application_url/path/of/the/request
The façade must not have a controller which maps to any possible path of the request, but must call the request on the correct backend server, based on a value in the header of the request. Then return this response to the user.
What I have so far is an implementation of the HandlerInterceptor. This interceptor works, however, I am not really happy with the way I need to avoid the afterCompletion by throwing an exception in the postHandle method.
If I do not throw an error, the default error page is appended to the correct response in the afterCompletion step.
This is my code so far:
public class RequestProcessingInterceptor implements HandlerInterceptor {
private final Logger log = LoggerFactory.getLogger(RequestProcessingInterceptor.class);
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
log.info("Doing some security stuff now ...");
log.warn("... security ok ... since I am not really checking stuff");
return true;
}
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
log.info("Forwarding request and sending that info back ...");
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource service = client.resource(UriBuilder.fromUri("http://localhost:8080").build());
response.setContentType("application/json");
response.getWriter().write(service.path(modelAndView.getModel().get("path").toString()).accept("application/json").get(String.class));
response.setStatus(200);
throw new Exception("Need to avoid the execution of the afterCompletion. Only way to do so is by throwing an exception...");
}
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
Is there a more proper way to intervene with the Spring livecycle or obtain the behaviour as described above?
Found a better solution. For what I need, I do not need to manipulate the results in an interceptor.
A much cleaner way is to define a Controller which maps with the request methods.
#RequestMapping(method = {RequestMethod.GET, RequestMethod.PUT, RequestMethod.POST})
public void handleRequest(HttpServletRequest request, HttpServletResponse response) { // code omitted }
You should not try to avoid the call to afterCompletion. Just implement an empty method and let SpringFramework call it.
Provided your controller returns null indicating that no view has to be called, it should work with a smoother Spring integration.
But I cannot understand why you use Spring MVC here. As you only interact with low level HttpServletRequest and HttpServletResponse, you could as well use :
a dedicated servlet in charge to relay the request and response to the backend and write the returned value in the response
a filter that would do the security stuff before passing request to filter chain

Resources