Adding Header in REST API breaks backward compatibility - rest

I have a REST interface defined as below
public interface UserService {
#GZIP
#POST
Response createUser(#HeaderParam("hostName") String hostName, User user);
}
I want to read two more headers - addToCache and useCache . The obvious solution is to add the headers in the above signature.
While that would work for explicit HTTP calls over rest clients, modifying the interface signature breaks the backward compatibility in functional test codebase which already connect to the above service using REST proxy service.
The only way out I see is to pass the two params inside the User object. Is there another way out?

You can inject header params as fields in the implementation class:
public interface UserService {
Response createUser(String hostName, User user);
}
#Path("/userservice")
public class UserServiceRestImpl implements UserService{
#HeaderParam("addToCache")
private String addToCache;
#HeaderParam("useCache")
private String useCache;
#GZIP
#POST
Response createUser(#HeaderParam("hostName") String hostName, User user){
System.out.println(addToCache);
System.out.println(useCache);
}
}
UPDATED:
try to disable auto scan and specify the resource explicitly:
<!-- disabled auto scanning
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
-->
<context-param>
<param-name>resteasy.resources</param-name>
<param-value>com.yourpackage.UserServiceRestImpl</param-value>
</context-param>
UPDATED2: Also you can try to inject #Context HttpHeaders instead of #HeaderParam:
private #Context HttpHeaders headers;
...
String addToCache = headers.getHeaderString("addToCache");

Related

Spring Boot Admin: custom header authentication

My app has a custom authentication mechanism based on a custom HTTP header. AFAIK, Spring Boot Admin supports only Basic auth and OAuth. But maybe there's a way for clients to supply some custom header along with their requests?
You can add the custom headers into existing headers by injecting the Bean as following.
#Bean
public HttpHeadersProvider customHttpHeadersProvider() {
return instance -> {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("X-CUSTOM", "My Custom Value");
return httpHeaders;
};
}
Alright, so if both SBA Server and SBA Client are launched along with the monitored application itself, and it has custom-headers security, we need to take care of 3 things:
As Nitin mentioned, one needs to register HttpHeadersProvider bean:
#Bean
public HttpHeadersProvider customHttpHeadersProvider() {
return instance -> {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("X-CUSTOM", "My Custom Value");
return httpHeaders;
};
}
Note, that these headers are not applied to OPTIONS requests to the Actuator endpoints, so one would either need to customize ProbeEndpointsStrategy, or disable Spring Security for OPTIONS calls to the management URL. Also, for some reason, I had to disable security for /actuator/health/**, although it should've been accessible with custom header provided:
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/actuator/**").antMatchers(HttpMethod.GET, "/actuator/health/**");
}
Finally, one needs to instantiate ApplicationRegistrator with a custom RestTemplate that would be pre-populated with a custom header:
#Bean
public ApplicationRegistrator registrator(ClientProperties client, ApplicationFactory applicationFactory) {
RestTemplateBuilder builder = new RestTemplateBuilder()
.setConnectTimeout(client.getConnectTimeout())
.setReadTimeout(client.getReadTimeout())
.additionalInterceptors((request, body, execution) -> {
request.getHeaders().set("X-CUSTOM", "My Custom Value");
return execution.execute(request, body);
});
if (client.getUsername() != null) {
builder = builder.basicAuthentication(client.getUsername(), client.getPassword());
}
return new ApplicationRegistrator(builder.build(), client, applicationFactory);
}

How to exclude RequestInterceptor for an specific Spring Cloud Feign client?

I have a number of clients for which a "global" RequestInterceptor has been defined. For one of the clients I need this "global" interceptor to be excluded. Is it possible to override the full set of RequestInterceptors for a particular FeignClient?
#FeignClient(value = "foo", configuration = FooClientConfig.class)
public interface FooClient {
//operations
}
#Configuration
public class FooClientConfig{
//How do I exclude global interceptors from this client configuration?
}
The spring-cloud-netflix version in use is 1.1.0 M5
It seems there is no easy way to override the global interceptor.
I think you could do it like this:
#Configuration
public class FooClientConfig{
#Bean
RequestInterceptor globalRequestInterceptor() {
return template -> {
if (template.url().equals("/your_specific_url")) {
//don't add global header for the specific url
return;
}
//add header for the rest of requests
template.header(AUTHORIZATION, String.format("Bearer %s", token));
};
}
}
Based on the issue stated here. Instead of excluding interceptors, you need to define different feign clients for each API. Add your interceptors based on your needs.
public class ConfigOne {
#Bean
public InterceptorOne interceptorOne(AdditionalDependency ad) {
return new InterceptorOne(ad);
}
}
Just make sure you don't use #Configuration annotation on above class.
Instead, importing this bean on client definition would be a working solution.
#FeignClient(name = "clientOne", configuration = ConfigOne.class)
public interface ClientOne { ... }
An enhanced way of solving this is to pass a custom header to your request like:
#PostMapping("post-path")
ResponseEntity<Void> postRequest(#RequestHeader(HEADER_CLIENT_NAME) String feignClientName, #RequestBody RequestBody requestBody);
I want to set the header in interceptor for only this feign client. Before setting the header, first, the interceptor checks HEADER_CLIENT_NAME header if exists and have the desired value:
private boolean criteriaMatches(RequestTemplate requestTemplate) {
Map<String, Collection<String>> headers = requestTemplate.headers();
return headers.containsKey(HEADER_CLIENT_NAME)
&& headers.get(HEADER_CLIENT_NAME).contains("feign-client-name");
}
Thus, you can check before setting the basic authentication. In interceptor:
#Override
public void apply(RequestTemplate template) {
if (criteriaMatches(template)) {
/*apply auth header*/
}
}
In this way, other feign client's requests won't be manipulated by this interceptor.
Finally, I set the feignClientName to the request:
feignClient.postRequest("feign-client-name", postBody);
One way to do this to remove the #Configuration annotation from the FooClientConfig class as in the current situation it is applied globally.
And then use
#FeignClient(value = "foo", configuration = FooClientConfig.class)
on all of the feign clients you want to use the config with.

How to log every URI access with the JSF servlet and the rest servlet

I'm using a default JSF servlet and RestEasy servlet to serve URI requests (Wildfly 8.1). I want every single URI request to be logged with a #SessionScoped backing bean. Either CDI bean (#Named) or ManagedBean (#ManagedBean) so that I can log the http requests from this visitor.
My requirements:
I don't want to invoke the logging of the access from each JSF page,
nor from each REST Resource Java file.
Every request must be linkable to #SessionScoped annotated backing bean Visit. The Visit object stores:
a user (if identified)
start of visit
an IP Address
n URI requests in a list: JSF resource requests and rest resource requests
My questions:
How do I register a filter in web.xml that logs both requests - be it JSF or REST - to the #SessionScoped annotated backing bean Visit?
If I could access this backing bean, how do I ensure that it is the session fo the same user? This session management of the web container is unclear to me. How does the web container map the request to a known session instance? By a default cookie?
Of course there is already a servlet-mapping on the url-pattern /* and one on /restresources/* One could not register 2 filters for the same path, could you? :
<filter>
<filter-name>UriLogger</filter-name>
<filter-class>com.doe.filters.UriAccessLogger</filter-class>
</filter>
<filter-mapping>
<filter-name>UriLogger</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Okay. For others that want to log every page and REST resource access, too.
Create the filter in the web.xml file.
<filter>
<filter-name>UriLogger</filter-name>
<filter-class>com.doe.filters.UriLoggingFilter </filter-class>
</filter>
<filter-mapping>
<filter-name>UriLogger</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Also, create the filter class.
package com.doe.webapp.controller.general.filters;
import java.io.IOException;
import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import com.doe.webapp.controller.general.VisitController;
#Named
#SessionScoped
public class UriLoggingFilter implements Serializable, Filter {
private static final long serialVersionUID = 1472782644963167647L;
private static Logger LOGGER = Logger.getLogger(UriLoggingFilter.class);
private String lastLoggedUri = "";
FilterConfig filterConfig = null;
#Inject
VisitController visitController;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
/**
* Log requests of interest with the VisitController.
*/
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException,
ServletException {
// Run the other filters.
filterChain.doFilter(request, response);
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String uri = httpServletRequest.getRequestURI();
String regex = "((/{1}\\w+$)|(/{1}\\w+\\.jsf$))";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(uri);
while (m.find()) {
LOGGER.info("match " + m.group());
if (!lastLoggedUri.equals(uri)) {
visitController.saveUriRequest(httpServletRequest);
lastLoggedUri = uri;
} else {
LOGGER.warn("Multiple URI access to the same resource of the same user: " + uri);
}
break;
}
}
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
}
In this code I removed the logging of repetitive requests. Only jsf page requests and REST resource requests are logged. Thus, no images, css or js requests. Adapt the RegEx according to your own needs. The EJB function saveUriRequest I have annotated with #Asynchronous, to avoid laggy delays of the response.
Answering my own questions:
The filter will pick up every single http request - be it a JSF page or REST resource call. Annotate the Filter as a CDI bean with #Named and #SessionScoped. Now you have a filter for every single visitor. A WORD OF CAUTION - DON'T DO THIS IF YOU HAVE HIGH NUMBER OF DIFFERENT USERS. THIS WILL RAPIDLY BRING DOWN YOUR AVAILABLE MEMORY. Alternatively you could mark it as #ApplicationScoped and get a visitor id from the ServletRequest request header instance and assign the request to a visitor. Also, this is prone to Denials-Of-Service attacks. (I'm using this only for internal purpose.)
Yes, the web container distriguishes between sessions by a jsessionid also from the ServletRequest request.
Hope this helps someone, too.

jaxrs/resteasy custom method paramater

I would like to have my JaxRs resource to take a custom method argument that is built from some parameter in the request.
Something to be used in conjunction with another object created from the body.
Something like:
#Resource
public class MyResource {
#Path("/resource")
public Object resource(MyResourceDTO body, AConfiguration conf){
}
}
For which the AConfiguration is created from some headers in the request.
How can I achive it?
I need something like th spring webargumentresovler: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/support/WebArgumentResolver.html
For my case MyResource is a subresource, the method should work also in this case...
If you add a DTO as parameter of your resource method your JAX-RS runtime will try to convert the body of the request into this type. You can additionally add any of the #xParam parameters like #QueryParam as parameters of your resource method. (The only exception is #FormParam as they are found in the body).
If you want to encapsulate multiple of your Params in one object you can use #BeanParam. Your Configuration class could look like this:
public class Configuration {
#QueryParam("foo")
private String foo;
#HeaderParam("bar")
private String bar;
// getters + setters
}
And can be used like this:
#POST
public Response someMethod(Dto dto, #BeanParam Configuration conf) {}
You can use something like below. Your conf object have be sent as json from the client. If the parameters in conf object have to change dynamically you have to follow the second approach.
#Resource
public class MyResource {
#POST
#Consumes("application/json")
#Path("/resource")
public Object resource(AConfiguration conf){
// This method can receive multiple objects here. Currently it receives
// conf object only as the payload of the post method.
}
}
To change the conf object dynamically, You can send json String.
public Object resource(String confJson){
// Collect parameters manually here.
}
In your pom.xml, you should include,
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>2.3.1.GA</version>
</dependency>
Edit:
You can set a json string as a header param (But, not the best practice.) Or you can set different headers at you will and access them using HttpHeaders. Here is an example.
public Object resource(#Context HttpHeaders confHeaders){
// Collect parameters manually.
String confJson = confHeaders.getRequestHeader("confJson").get(0);
// cast your `confJson` to `AConfiguration aConf` here.
// process query params and set them to aConf here.
}

GWTP HttpRequest in Action Handler to get session

I need to access HttpRequest object in Action handlers in server side of GWTP.
how can I access this information.
For every interaction with server, I need request object.
Please help me.
Using RPC with servlets its absolutely fine we can, but I need it with ActionHandler in GWTP.
public class MyHandler extends
AbstractActionHandler<MyAction, MyResult> {
#Inject
Provider<HttpServletRequest> requestProvider;
#Override
public MyResult execute(MyAction action, ExecutionContext context) throws ActionException
{
HttpSession session = requestProvider.get().getSession();
}
}
If you use Spring, you need also
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
in web.xml