I am using Feign to make my http call. For security reason, I need to generate and add header on each request.
Headers values may differ on each request (for example, it use current connected user in session).
With Feign we can define a requestInterceptor to handle header generation.
But my problem, I that a requestInterceptor is defined when FeignBuilder is used. But after building my client, I can't access easily to this interceptor (except keep a reference on it).
Ex:
MyApi api = Feign.builder()
.requestInterceptor(new SecuringRequestInterceptor("some static value"))
.target(MyApi.class, "http://....");
How can I add some per request specific value to this interceptor to let it generate some kind of header based on these values ?
I have try to keep a reference on the requestInterceptor instance and create a method to update his status. But I don't think it is thread safe in a concurrent environment :
class SecuringRequestInterceptor implements RequestInterceptor {
private String staticValue;
private String dynamicValue;
public SecuringRequestInterceptor(String value) {
this.staticValue = value;
}
public void update(String value) {
this.dynamicValue = dynamicValue;
}
#Override
public void apply(RequestTemplate template) {
// use dynamic value here
template.header("sign", this.staticValue + "_" + this.dynamicValue);
}
}
thanks for your help.
Related
I have a simple Spring Boot REST service for the IFTTT platform. Each authorized request will contain a header IFTTT-Service-Key with my account's service key and I will use that to either process the request or return a 401 (Unauthorized). However, I only want to do this for select endpoints -- and specifically not for ANY of the Spring actuator endpoints.
I have looked into Spring Security, using filters, using HandlerInterceptors, but none seem to fit what I am trying to do exactly. Spring security seems to come with a lot of extra stuff (especially the default user login), filters don't really seem to match the use case, and the handler interceptor works fine but I would have to code logic in to watch specific URLs and ignore others.
What is the best way to achieve what I am trying to do?
For reference, this is the code I have now:
public class ServiceKeyValidator implements HandlerInterceptor {
private final String myIftttServiceKey;
public ServiceKeyValidator(#Value("${ifttt.service-key}") String myIftttServiceKey) {
this.myIftttServiceKey = myIftttServiceKey;
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// TODO will have to put logic in to skip this when actuator endpoints are added
String serviceKeyHeader = request.getHeader("IFTTT-Service-Key");
if (!myIftttServiceKey.equals(serviceKeyHeader)) {
var error = new Error("Incorrect value for IFTTT-Service-Key");
var errorResponse = new ErrorResponse(Collections.singletonList(error));
throw new UnauthorizedException(errorResponse);
}
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}
You need to add filtering for the required endpoints in the place where you register your HandlerInterceptor.
For example:
#EnableWebMvc
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(
new ServiceKeyValidator())
.addPathPatterns("/ifttt/**")
.excludePathPatterns("/actuator/**");
}
}
You can use different URLs path matchers to filter which URL endpoints must be handled by your interceptor and which are not. As the method addPathPatterns returns InterceptorRegistration object that configures this.
In my APIs, there is custom request class for each APIs, I want to write code which gets fields from HttpHeaders from upcoming request and set that set of fields to that particular Request class, so it will do this for all request classes.
I have done this in MVC code, but don't know how to do this for reactive APIs with WebFlux(Library- Project Reactor).
Controller:
public Mono<ResponseEntity<JsonNode>> getData(#RequestHeader HttpHeaders header, GetDataRequest request){
.... // all stuff
}
now some data are coming from header like type, token, comID, etc.
I want to set these fields to Request Class GetDataRequest before further processing of the request as I will need these fields further,
but this request class different for all the requests, so I need common code, which set this to any request class which is passed to it.
Note: not using WebClient here, only Flux and Mono are there.
So basically, get fields from a header which is of type HttpHeaders, set these data to particular request class, but do this in WebFlux Framework, reactive APIS.
Please help anyone.
I would do the following:
define some base class for your requests that would have attributes you want to store headers values in, e.g.:
public class MyAbstractRequest {
private String header1;
private String header2;
// ...
// getters and setters
}
inherit all you request classes from this class, e.g.:
public class GetDataRequest extends MyAbstractRequest {
// GetDataRequest content here
}
create an argumentResolver for all those classes that inherit from MyAbstractRequest. To ensure the behavior is same as for normal request body deserialization use AbstractMessageReaderArgumentResolver as a base class:
public class MyArgumentResolver extends AbstractMessageReaderArgumentResolver {
public MyArgumentResolver(List<HttpMessageReader<?>> messageReaders, ReactiveAdapterRegistry adapterRegistry) {
super(messageReaders, adapterRegistry);
}
#Override
public boolean supportsParameter(MethodParameter parameter) {
return MyAbstractRequest.class.isAssignableFrom(parameter.getParameterType());
}
#Override
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange) {
return readBody(parameter, true, bindingContext, exchange)
.map(o -> {
// your headers extraction logic here ...
((MyAbstractRequest) o).setHeader1(exchange.getRequest().getHeaders().getFirst("header1"));
((MyAbstractRequest) o).setHeader2(exchange.getRequest().getHeaders().getFirst("header2"));
return o;
});
}
}
configure your MyArgumentResolver in the webflux configuration:
#Configuration
public class WebFluxConfiguration implements WebFluxConfigurer {
#Autowired
ApplicationContext applicationContext;
#Override
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
ServerCodecConfigurer serverCodecConfigurer = applicationContext.getBean(ServerCodecConfigurer.class);
ReactiveAdapterRegistry reactiveAdapterRegistry = applicationContext.getBean("webFluxAdapterRegistry", ReactiveAdapterRegistry.class);
configurer.addCustomResolver(new MyArgumentResolver(serverCodecConfigurer.getReaders(), reactiveAdapterRegistry));
}
}
Now your requests should get injected into the controller methods with the configured resolver:
public Mono<ResponseEntity<JsonNode>> getData(GetDataRequest request){
}
I'm new to Spring, and since Spring provides many ways to map an HTTP request to Java objects, I'm hoping someone could advice me how to resolve this:
I have a client that sends a request having
ContentType: application/x-www-form-urlencoded
Some of the request parmeters have names such as
"form_data[orderStatus]", "form_data[orderNumber]", etc'
I have no control over this client!
I have a java class (#Component) called MyOrder which looks as follows:
#component
#Scope("prototpe")
public class MyOrder {
private String orderStatus;
private String orderNumber;
//etc'
public void setOrderStatus(String orderStatus) {
this.orderStatus = orderStatus;
}
//public setter for all other properties, as the above
}
What is the simplest way to create an instance of MyOrder
populated with all values of all "form_data[]", so that I can have a controller method having a signature that includes a MyOrder parameter, such as:
public ModelAndView saveNewOrder( #RequestParam("foo") String foo,
#ModelAttribute("myOrder") MyOrder anOrder) {
//... impl'n here
}
The best solution I could think of was to use a Web Filter which would flaten request params names such as "form_data[attrib1]" to "attrib1", and then Spring would do all the work of populating the MyOrder instance.
One disadvantage of this is that the request may have both "form_data[attrib1]" and "attrib1" parameters. For example:
form_data[orderStatus]=ok
orderStatus=fail
In this case i want MyOrder.orderStatus to have the value "ok".
Any good way of utilizing Spring create MyOrder from the request?
As an alternative, that does not use the class MyOrder, is there a way to have Spring map all the form_data[] parameters and their values to a map, so that i can have the controller method below?
public ModelAndView saveNewOrder( #RequestParam("foo") String foo,
<some annotation> #Map<String,String> formFieldsOfAnOrder) {
//... impl'n here
orderStatus = formFieldsOfAnOrder.get("orderStatus");
//or at least:
orderStatus = formFieldsOfAnOrder.get("form_data[orderStatus]");
}
I am working with retrofit and need to be able to use multiple interceptors. Currently I am using one to automatically append an auth token but i need to be able to make calls with no auth token. If i add another interceptor with no auth token in the header how do I use that one instead of the auth token interceptor.
val interceptor: Interceptor = Interceptor { chain ->
val newRequest = chain.request().newBuilder().
addHeader("Auth_Token", pref.getString(PSPreferences.prefAuthKey, "")).
cacheControl(CacheControl.FORCE_NETWORK).
build()
chain.proceed(newRequest)
}
okHttpClient = OkHttpClient.Builder().
readTimeout(1, TimeUnit.MINUTES).
connectTimeout(1, TimeUnit.MINUTES).
addInterceptor(interceptor).build()
val retrofitInstance = Retrofit.Builder()
.baseUrl(APIEndpointInterface.BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
apiInterface = retrofitInstance.create<APIEndpointInterface>(APIEndpointInterface::class.java)
OkHttpClient maintains a list of the interceptors which you can access, however it is an unmodifiable collection.
This leaves us with three options I believe:
Create two OkHttpClient instances, and by deduction two Retrofit
instances, one for the unauthenticated requests, and one for the
authenticated requests.
Check if you should use the interceptor, e.g. in your authentication interceptor, you can first check if there exists a key in your preferences for the token, and if so use it; if not, you simply proceed without modifying anything. You do this for your unauthenticated interceptor too. I think this is the easiest solution for your case.
Create a single interceptor, which will maintain a modifiable list
of interceptors which you can add and remove at will. You would need
to keep a reference to this interceptor, maybe make it a Singleton.
For the third option, I have provided a very simple example:
public class HttpRequestResponseInterceptor implements Interceptor {
public final List<RequestInterceptor> requestInterceptors = new ArrayList<>();
public final List<ResponseInterceptor> responseInterceptors = new ArrayList<>();
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
for (RequestInterceptor interceptor : requestInterceptors) {
request = interceptor.intercept(request);
}
Response response = chain.proceed(request);
for (ResponseInterceptor interceptor : responseInterceptors) {
response = interceptor.intercept(response);
}
return response;
}
public interface RequestInterceptor {
Request intercept(Request request) throws IOException;
}
public interface ResponseInterceptor {
Response intercept(Response response) throws IOException;
}
}
In this case you would need to implement the custom interfaces RequestInterceptor and ResponseInterceptor.
An example of what an implementation of these interfaces would look like:
public class ExampleInterceptor implements HttpRequestResponseInterceptor.RequestInterceptor,
HttpRequestResponseInterceptor.ResponseInterceptor {
#Override
public Request intercept(Request request) throws IOException {
return request.newBuilder().addHeader("REQUEST_HEADER", "EXAMPLE").build();
}
#Override
public Response intercept(Response response) throws IOException {
return response.newBuilder().addHeader("RESPONSE_HEADER", "EXAMPLE").build();
}
}
You would then need to add this interceptor to our main interceptor twice, once to requestInterceptors and once to responseInterceptors (or only to one of these if it intercepts only requests or only responses).
This example is far from complete. The benefit of this solution is that it adds the ability to add and remove interceptors without having to recreate the OkHttpClient instance. It requires extra work if you want to support retrying requests, for example.
Short version: How do I get HttpServletRequest.getRemoteUser() to return the username when I am using a custom authentication filter?
Long version:
I am modifying a Tomcat application that currently uses declarative security (web.xml & tomcat-users.xml) to instead use a custom (written by me) authentication filter (derived from javax.servlet.Filter). There is a lot of information out there on how to do this and it looks very straightforward.
However, the existing application makes calls to HttpServletRequest.getRemoteUser(), and I assume that unless I do something to set this property in my filter, it will return null. I cannot find any information on how to populate the getRemoteUser() property in a filter (there is no setRemoteUser()). I found a post out there that recommends wrapping the request object in the filter. I will do this if I have to, but I am hoping there is a less invasive way to accomplish this.
Can anyone help?
Yes, the only way to modify an HttpServletRequest or HttpServletResponse is to decorate it and provide your own implementation for the methods of interest by overriding them. This is a standard pattern with authentication filters and that is the purpose of HttpServletRequestWrapper (the response counterpart is HttpServletResponseWrapper). We do it this way to wrap a kerberized request, as follows
public class KerbHttpServletRequest extends HttpServletRequestWrapper
{
private Principal myPrincipal;
private String myAuthType;
public KerbHttpServletRequest(HttpServletRequest aRequest,
Principal aPrincipal,
String aAuthType)
{
super(aRequest);
myPrincipal = aPrincipal;
myAuthType = aAuthType;
}
/**
* This method returns the Remote User name as user\#domain.com.
*/
#Override
public String getRemoteUser()
{
return myPrincipal.getName();
}
#Override
public String getAuthType()
{
return myAuthType;
}
#Override
public Principal getUserPrincipal()
{
return myPrincipal;
}
}