Custom default headers for REST API only using Spring Data REST - spring

I have a use case where my application hosts REST API and web application and we need to add custom header to REST APIs only. REST APIs are enabled through Spring Data REST. Typically we could use Servlet Filter to achieve this but we need code the logic of isolating requests to our REST API and add the custom headers. It would be nice if Spring Data REST API allows to add a default header to all the responses it generates. What are your thoughts? Don't say I am lazy :)

For folks looking for actual implementation details..
Interceptor
public class CustomInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("adding CORS headers.....");
response.addHeader("HEADER-NAME", "HEADER-VALUE");
return true;
}
}
Java Configuration
#Configuration
public class RepositoryConfig extends
RepositoryRestMvcConfiguration {
#Override
public RequestMappingHandlerMapping repositoryExporterHandlerMapping() {
RequestMappingHandlerMapping mapping = super
.repositoryExporterHandlerMapping();
mapping.setInterceptors(new Object[] { new CustomInterceptor() });
return mapping;
}
}

As Spring Data REST is built on top of Spring MVC, the easiest way is to configure a custom HandlerInterceptor as described in the reference documentation.
With Spring Data REST the easiest way is to extend RepositoryRestMvcConfiguration and override repositoryExporterHandlerMapping, call the parent method and then invoke ….setInterceptors(…) on it.

Finally I managed to make the setup of custom interceptor working also on spring-data-rest 2.4.1.RELEASE.
#Configuration
public class RestMvcConfig extends RepositoryRestMvcConfiguration {
#Autowired UserInterceptor userInterceptor;
#Autowired ApplicationContext applicationContext;
#Override
public DelegatingHandlerMapping restHandlerMapping() {
RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(resourceMappings(), config());
repositoryMapping.setInterceptors(new Object[] { userInterceptor }); // FIXME: not nice way of defining interceptors
repositoryMapping.setJpaHelper(jpaHelper());
repositoryMapping.setApplicationContext(applicationContext);
repositoryMapping.afterPropertiesSet();
BasePathAwareHandlerMapping basePathMapping = new BasePathAwareHandlerMapping(config());
basePathMapping.setApplicationContext(applicationContext);
basePathMapping.afterPropertiesSet();
List<HandlerMapping> mappings = new ArrayList<HandlerMapping>();
mappings.add(basePathMapping);
mappings.add(repositoryMapping);
return new DelegatingHandlerMapping(mappings);
}
}
I had to override the restHandlerMapping method, copy-paste it's content and add a line repositoryMapping.setInterceptors for adding custom interceptor, in my case the UserInterceptor.
Is there any better way?

Related

Control #RestController availability programmatically

Is it possible to control a #RestController programmatically to enable it or disable it? I don't want to just write code in each #RequestMapping method to do some kind of if (!enabled) { return 404Exception; }
I've seen this question but that works only at startup time. What I need is really something that would allow me to enable or disable the controller multiple times.
I've thought of different ways but don't know which are doable in spring.
Actually control the container (jetty in my case) so requests to that particular endpoint are disabled
Somehow control RequestMappingHandlerMapping since it seems to be that class that does the mapping between urls and controllers
control the lifecycle of the #RestController component so that i can create it and destroy it at will, but then i'm not sure how to trigger the mapping to the endpoint
If the end result is that you want to respond with a 404 when you decide that a specific endpoint should be disabled then you could write an interceptor which checks whether your enabled condition is false and, if so, sets the response accordingly.
For example:
#Component
public class ConditionalRejectionInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String requestUri = request.getRequestURI();
if (shouldReject(requestUri)) {
response.setStatus(HttpStatus.NOT_FOUND.value());
return false;
}
return super.preHandle(request, response, handler);
}
private boolean shouldReject(String requestUri) {
// presumably you have some mechanism of inferring or discovering whether
// the endpoint represented by requestUri should be allowed or disallowed
return ...;
}
}
In Spring Boot, registering your own interceptor just involves implementing a WebMvcConfigurerAdapter. For example:
#Configuration
public class CustomWebMvcConfigurer extends WebMvcConfigurerAdapter {
#Autowired
private HandlerInterceptor conditionalRejectionInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
// you can use .addPathPatterns(...) here to limit this interceptor to specific endpoints
// this could be used to replace any 'conditional on the value of requestUri' code in the interceptor
registry.addInterceptor(conditionalRejectionInterceptor);
}
}

Spring Annotation-Based Interceptor

can any of you please post a snippet for the following. I've looked in a couple of places but they all seem to be xml based. I'd like to have this code in java configuration style only.
I am trying to accomplish the following..
1) I need to intercept a specific url, for example "http://localhost:8080/test" and only a url that starts with "/test/*". This is not a spring security question, this is just for an endpoint I like to intercept.
2) I need to intercept the HttpServletRequest object of that request and add a specific header to that request. For example, "authorization", "bearer xxxx".
3) When the /test endpoint finally hits, I should be able to see the authorization header inside my HttpServletRequest object.
Thank you guys.
you can do this like this:
(Whithin "YourOwnInterceptor" you can add the value to the header...)
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Bean
public YourOwnInterceptor yourOwnInterceptor() {
return new YourOwnInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
super.addInterceptors(registry);
registry.addInterceptor(yourOwnInterceptor()).
addPathPatterns("/<your-url-to-intercept>/**");
}
}

Simple Reverse Proxy with Spring Boot and Netflix Zuul

I'm looking to implement a simple reverse proxy with Spring Boot that is:
Easy to add routes
Ability to add custom authentication on a per route basis
Add additional headers as needed
I've looked at the facilities provided by the #EnableZuulProxy annotation but it seems too heavyweight as I don't have a desire to use Eureka, Ribbon, or Hystrix. However, #EnableZuulServer is a bit light on configuration.
Would anyone be able to provide an example of what I'm after? Is Netflix Zuul the right choice for this or is there another library I should be looking at?
Thanks!
Simple Reverse Proxy Server
It's easy to set up a simple proxy reverse using Spring Boot without Ribbon, Eureka, or Hystrix.
Simply annotate your main application class with #EnableZuulProxy and set the following property in your configuration:
ribbon.eureka.enabled=false
Then define your routes in your configuration like such:
zuul.routes.<route_name>.path=<route_path>
zuul.routes.<route_name>.url=http://<url_to_host>/
where <route_name> is an arbitrary name for your route and <route_path> is a path using Ant-style path matching.
So a concrete example would be something like this
zuul.routes.userservice.path=users/**
zuul.routes.userservice.url=http://localhost:9999/
Custom Filters
You can also implement your custom authentication and any additional headers by extending and implementing the ZuulFilter class and adding it as an #Bean to your #Configuration class.
So another concrete example:
public class MyFilter extends ZuulFilter {
#Override
public String filterType() {
// can be pre, route, post, and error
return "pre";
}
#Override
public int filterOrder() {
return 0;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
// RequestContext is shared by all ZuulFilters
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
// add custom headers
ctx.addZuulRequestHeader("x-custom-header", "foobar");
// additional custom logic goes here
// return isn't used in current impl, null is fine
return null;
}
}
and then
#Configuration
public class GatewayApplication {
#Bean
public MyFilter myFilter() {
return new myFilter();
}
}
Zuul is a good choice. Am not sure about other alternatives but, we've started building Zuul filters (Pre/Post and Route) that could intercept the request and do all pre/post processing and route based upon your need. It is not mandatory to use the whole bunch of Eureka, Ribbon and Hysterix along with Zuul.

Add interceptor to spring boot mongodb rest example

I am trying to add an interceptor to a simple Spring-boot-mongodb-rest app, as can be seen here : http://spring.io/guides/gs/accessing-mongodb-data-rest/, in order to perform certain actions after the default rest handler is invoked. Here is my MongoRepository, whose CRUD operation is called upon a POST request to the server:
#RepositoryRestResource(collectionResourceRel = "reminder", path = "reminder")
public interface ReminderRepository extends MongoRepository<Reminder, String> {
List<Reminder> findBySendee(#Param("sendee") String sendee);
}
I am trying to register an interceptor for all HTTP requests by extending the WebMvcConfigurerAdapter class like this:
#Configuration
#ComponentScan
public class RemindxWebConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new RemindxInterceptor());
}
}
As mentioned in the spring boot docs, I have not added the #EnableWebMvc annotation to this. While running the application, the addInterceptors function does get called and adds the interceptor. However, the given interceptor is not called after the POST handler is invoked. I am unable to figure out a way to have spring use this RemindxWebConfig for all MongoRepository http requests. Any inputs are appreciated.

What does the #Secure annotation do and what package is it apart of

I'm writing an API using Java EE, JAX-RS, Jersey. In doing this I've implemented my own security context and security filter.
Looking at questions like this one (How to get MIME type of uploaded file in Jersey) I've seen the #Secure annotation but what does it do? My hope was that is was an annotation that queries the isSecure method of the security context in the same way that #RolesAllowed does for checking if a user has the right to access a particular method. If so is there such a way of doing so with annotations or am I stuck to using the #Context to get the security context and just from that.
The #Secure annotation seems to be a custom one. JAX-RS/Jersey does not support such feature out-of-the-box but it's not that hard to implement. Lets say you have your own #Secure annotation and you want to do checks whether a communication channel is secure for methods annotated with this annotation. You need to create a custom ResourceFilterFactory in which you'll assign a special filter for such methods:
public class IsSecureResourceFilterFactory implements ResourceFilterFactory {
private class IsSecureFilter implements ResourceFilter, ContainerRequestFilter {
// ResourceFilter
#Override
public ContainerRequestFilter getRequestFilter() {
return this;
}
#Override
public ContainerResponseFilter getResponseFilter() {
return null;
}
// ContainerRequestFilter
#Override
public ContainerRequest filter(final ContainerRequest request) {
// Check whether the channel is secure.
if (request.isSecure()) {
return request;
}
// Throw an exception if it's not.
throw new WebApplicationException(Response.Status.FORBIDDEN);
}
}
#Override
public List<ResourceFilter> create(final AbstractMethod abstractMethod) {
// Add IsSecureFilter for resource methods annotated with #Secure annotation (ignore other resource methods).
return abstractMethod.isAnnotationPresent(Secure.class)
? Collections.<ResourceFilter>singletonList(new IsSecureFilter()): null;
}
}
Now you need to tell Jersey about this ResourceFilterFactory. There are 2 ways:
via web.xml
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>my.package.IsSecureResourceFilterFactory</param-value>
</init-param>
or via META-INF/services mechanism - you need to create a file called META-INF/services/com.sun.jersey.spi.container.ResourceFilterFactory which would contain a fully qualified name of your factory (in this case my.package.IsSecureResourceFilterFactory) and make sure this file is on the class-path of your application.

Resources