What's the most immediate way to just proxy a http request via a filter/interceptor? - spring-boot

For my project I need to implement a simple proxy in spring boot by using either a filter or an interceptor. So, my microservice A will just receive a http request meant for microservice B, will forward it with all headers/query params, and return the response either good or bad with the proper response code and any response header microservice B will add.
I know there are ready to use libraries like spring-cloud-gateway or spring-cloud-zuul but they both conflict with other specifications I have and cannot use them. Moreover I don't really need any filtering/aggregation logic, I just want to make a simple proxy.
I tried doing the following:
#Component
#Order(1)
public class ByPassFilter extends OncePerRequestFilter {
#Value("${proxy.active}")
private Boolean active;
#Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
if(active){
RestTemplate rest = new RestTemplate(); //overkill here?
HttpMethod method = HttpMethod.resolve(request.getMethod());
switch(method){
case GET:
//use rest template to do get
}
}else{
filterChain.doFilter(request, response);
}
}
But RestTemplate seems very complicated as it is supposed to be used when I want to handle/process the request, it will even return exception if the code is not 200. I also have no idea on how to write the output directly to the HttpServletResponse.
So, is there any other way even more transparent? I saw people using apache's OkHttpClient, is that a little more generic than RestTemplate?

Related

How to disable specific headers in Spring Boot

Is it possible to disable following headers in Spring Boot?
X-Forwarded-Host:
X-Host:
X-Forwarded-Server:
Following did not work for me
class MyFilter extends OncePerRequestFilter {
#Override
public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, new HttpServletResponseWrapper((HttpServletResponse) response) {
public void setHeader(String name, String value) {
if (!name.equalsIgnoreCase("X-Forwarded-Host")) {
super.setHeader(name, value);
}
}
});
}
Let's try to have a look broader and start to think about request-response lifecycle.
Once a request has been initiated by a client, there are sort of stops and layers that the request/response goes through between client and the application. There might be a firewall, load-balancer, reverse proxy, middleware etc. On the other hand, based on the application server which serves the application, those headers might be added as well. If there is a mechanism which adds or removes or rewrites the headers apart from the application, those headers should have been managed out of the application.
That being said, if headers were added by the application, they could have been managed within the application. But if headers were added by another stop or layer, they should have been managed in a particular configuration.
Apart from the headers in general, if we think about these specific headers: Based on my general experience, the headers you provided are added when there is a reverse proxy between client and application.
You can leverage more information about them: https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#x-headers
To sum up, you should be managing those headers according to how and why they have been added.
If you want to disable all default headers you can do the folowing:
#EnableWebSecurity
public class WebSecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers(headers -> headers
// do not use any default headers unless explicitly listed
.defaultsDisabled()
.cacheControl(withDefaults())
);
return http.build();
}
}
To disable specific ones you can follow the same strategy.
Reference: https://docs.spring.io/spring-security/reference/5.8/servlet/exploits/headers.html#page-title

With Spring Security how do i determine if the current api request should be authenticated or not?

With spring security you can have public api endpoints that are accessible by everyone and endpoints that need to be authenticated before getting a response. In my app users authenticate via a jwt token. For logged in users right now the token is always checked, regardless of whether a public api endpoint gets the request or not.
I would like to know how to check if the current endpoint is a public endpoint or a authenticated one, that way i can alter the code so that the token checking is only done when the endpoint requires authentication.
I could add all public endpoints in a hashset and compare the current request endpoint with the public ones but that isn't efficient and also, some of the public endpoints contain wildcards (**) so that would make comparing a bit of a hassle.
This is the only information i could find:
Spring Security - check if web url is secure / protected
but its about JSP.
I can't get the request information from SecurityContextHolder.getContext() either. My guess is that i should get the information from org.springframework.security.config.annotation.web.builders.HttpSecurity because that is the same class used to define which endpoints don't require authentication. (with anthMatchers().permitall()). But i don't know which method to invoke and i'm not sure if HttpSecurity can even be autowired into another class. Can anyone give me some pointers?
Thank you
Assuming that you're using a separate filter for the token check, you can avoid the token check for public endpoints by overriding the protected boolean shouldNotFilter(HttpServletRequest request) method of the OncePerRequestFilter in your JwtTokenFilter. By default, this method will always return false. So all requests will get filtered. Overriding this method to return true for the public endpoints will give you the desired functionality.
And to check the requests with the wildcards(**), you can use AntPathRequestMatcher. So, you can do something like below.
public class JwtTokenFilter extends OncePerRequestFilter {
private static RequestMatcher requestMatcher;
public static void ignorePatterns(String... antPatterns) {
List<RequestMatcher> matchers = new ArrayList<>();
for (String pattern : antPatterns) {
matchers.add(new AntPathRequestMatcher(pattern, null));
}
requestMatcher = new OrRequestMatcher(matchers);
}
static {
final String[] publicEndPoints = {"/public-api/**","/resources/**"};
ignorePatterns(publicEndPoints);
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return requestMatcher.matches(request);
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
....
}
}
Hope this helps!!

Earliest point to encrypt json payload in spring mvc application

I have an spring boot application serving restful api's.
I'd like to make sure that certain fields are masked / encrypted at the earliest possible time so that they are not shown in clear text in the application log ... via logback.
Is there an entry point / filter / sprint aspect I can implement so a to achieve this ?
As cearly explained in Ali Dehghani's Answer in this post the best place to do what you want to, is in a Response filter. So you have to write a class that implements the Filter interface and you filter your response in the doFilter method.
#Component
public class YourResponseFilter implements Filter {
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
...
// do your work there
}
}
You may or may not use the annotate your filter with #Component, depending in the fact if you want to filter all your reponses or not.
If you need more help let me know.

Logging all request and response in Spring Boot REST service

I use Spring boot and have some REST controllers. I want to logging all request and response. I using external tomacat, not embeded! I write Interceptor:
#Component
#Log4j2
public class LoggingWebMvcInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final ContentCachingRequestWrapper wrapper = new ContentCachingRequestWrapper(request);
log.debug("REQ!!!! {}", wrapper.getReader().lines().collect(Collectors.joining(System.lineSeparator())));
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//log.debug("Response: {}", response);
}
And adding his:
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final LoggingWebMvcInterceptor loggingWebMvcInterceptor;
#Autowired
public WebMvcConfig(LoggingWebMvcInterceptor loggingWebMvcInterceptor) {
this.loggingWebMvcInterceptor = loggingWebMvcInterceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingWebMvcInterceptor);
}
}
But It don't work!
When I try POST request, his logging, but I have error: "Required request body is missing.
What am I doing wrong? I created a wrapper for the request!
I need to completely log all requests (POST, GET, DELETE, PUT) with headers and body and all responses. How can i do this? Any help, please.
Although your problem is not every well understood (not documented well -- for example where this is coming from is not shown Required request body is missing.) but anyways.
For logging purposes, I would not go with an Interceptor as I feel that this is too much work. Instead you could very well create an Aspect with a pointcut defined to around methods annotated with the various Spring controller annotation. The ProceedingJoinPoint#proceed method effectively allows you to grab the response object and the request itself contains all the information needed regarding parameters, IP, methods and so on.
With that in hand, you could then inject a HttpServletRequest in there, thus ending up having all the right tools to perform any logging activities.
Adding the caching wrapper is something very correct indeed if you would like to cache and re-read the HttpServletRequest's body multiple time but I would avoid adding it in the Interceptor/Aspect itself.
According to Baeldung documentation, ContentCachingRequestWrapper class has these limitations:
ContentCachingRequestWrapper class only supports the following:
Content-Type:application/x-www-form-urlencoded
Method-Type:POST
and
We must invoke the following method to ensure that request data is cached in ContentCachingRequestWrapper before using it: requestCacheWrapperObject.getParameterMap();
https://www.baeldung.com/spring-http-logging
You can use a web Filter (javax.servlet.Filter) as :
public class CustomFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain){
//Log actions heres
chain.doFilter(req, resp);}}
Then declare your filter in web.xml as :
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>package.CustomFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Passing a new header in Spring Interceptor

I want to add authentication logic to interceptor. When service is called, interceptor will authenticate. Once authenticated, I want to put a new header in the request say 'header-user': 'john-doe'. But in interceptor, I am unable to do that, when I add to response.setHeader(), nothing happens.
I want to use this new header in actual REST service.
public class AuthInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Authenticate
// Add header
response.setHeader("header-user", "john-doe"); // not working
return true;
}
...
}
If I add Filter, filter is called before Interceptor.
I figured out from Using Spring Interceptor that I can use setAttribute
request.setAttribute("user", "john-doe");
In controller side use,
public String testService(#RequestAttribute("user") String user){

Resources