Passin Parameters from Filter to Business Service in SpringBoot - spring

I have 3 REST services which are reading some common header parameters on the request. I need to use that parameters on my business services. instead of reading that common header parameters on each web service controller (#RestController), Is it possible to read that headers on request filter and make it available on the business services ? If yes, are there any examples to do this ?

You can get request object
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
and access the headers in business services using request object.

Like #Nitin suggest you can pass the request object from your controllers to your services and read the header there. There is no problem with that.
If you still want to read it in a filter and have it available in any #Service you can do as follows:
#Component
#Order(1)
public class HeaderReaderFilter implements Filter {
#Autowired
private HeaderDataHolder headerDataHolder;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
headerDataHolder.setHeaderContent(httpRequest.getHeader("header_field"));
chain.doFilter(request, response);
}
}
#RequestScope
#Component
public class HeaderDataHolder {
private String headerContent;
public String getHeaderContent() {
return headerContent;
}
public void setHeaderContent(String headerContent) {
this.headerContent = headerContent;
}
}
And then have the HeaderDataHolder #Autowired in your service classes. Notice the necessary #RequestScope so you have a different bean for each request.

Related

Custom Authentication Filter for Mule Http Listener

I would like to implement a Security Filter in Mule 3 extending OncePerRequestFilter from Spring Security. But I am unable to make it work because the code for authentication hadn't been reach.
Here is the code for registering the filter on mule xml.
<spring:bean id="requestContextFilter" class="com.sample.authentication.BeforeBasicAuthFilter"/>
<ss:http entry-point-ref="restAuthenticationEntryPoint">
<ss:custom-filter ref="requestContextFilter" before="FIRST"/>
</ss:http>
public class BeforeBasicAuthFilter extends OncePerRequestFilter {
private HttpServletRequest request;
public HttpServletRequest getRequest() {
return request;
}
public void setRequest(HttpServletRequest request) {
this.request = request;
}
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
this.request = request;
System.out.println("XXXX ");
filterChain.doFilter(request, response);
}
}
I believe that that filter is not registered to be used by the http listener or authentication manager. Was there a way in Mule to register a filter to be used on a http Listener? My goal is that I should be able to intercept the raw http request before the authentication will take place.
Use before BASIC_AUTH_FILTER in the custom-filter.
<ss:http entry-point-ref="restAuthenticationEntryPoint">
<ss:custom-filter ref="requestContextFilter" before="BASIC_AUTH_FILTER"/>
</ss:http>

Keycloak spring boot microservices

i have a few java micro services deployed on open shift . all of them are protected by a api-gateway application which uses keycloak for authentication & Authorization.
Down stream services need to log which user perform certain actions.
in my api-gateway application properties i have already set zuul.sensitiveHeaders to empty
zuul.sensitiveHeaders:
i can see bearer token in the downstream applications .
but how do i get the principal/user from token as downstream applications don't have keycloak dependency in gradle. ( if i add the dependency , i need to reconfigure realm and other properties ) .. is this the right way to do ?
i also tried adding a filter in api-gateway to separately set the user_name in header
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
System.out.println(" Filter doFilter "+req.getUserPrincipal());
if(req.getUserPrincipal() != null ){
res.setHeader("MYUSER",req.getUserPrincipal()==null?"NULL":req.getUserPrincipal().getName());
}
chain.doFilter(request, response);
}
But when i try to get the header in downstream microservices is null.
I wouldn't recommend doing this, or assuming that your non-web facing apps are completely secure. Realistically you should be re-validating the bearer token.
What you need is a zuul filter to add a header to the request. This is mostly from memory and you could update the filter to check if it should filter or not, that the request doesn't already contain an expected header etc.
#Component
public class AddUserHeader extends ZuulFilter {
private static final Logger LOG = LoggerFactory.getLogger(AddUserHeader.class);
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 0;
}
#Override
public boolean shouldFilter{
return true;
}
#Override
public Object run() {
RequestContext.getCurrentContext().addZuulRequestHeader("MYUSER", SecurityContextHolder.getAuthentication().getPrincipal().getName());
return null;
}

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>

How to modify Http headers before executing request in spring boot mvc

I have multiple rest endpoints. userId (http header) is common in all endpoints. I want to apply a logic, let say set its default value if not provided or trim it if is provided in request before request enters the method (for eg: heartbeat). How can we achieve this in spring boot rest mvc.
#RestController
public class MyResource {
#RequestMapping(value = "/heartbeat", method= RequestMethod.GET)
public String heartbeat (#RequestHeader (value="userId", required=false) String userId)
{
...
}
}
Can you try this?
#Configuration
#Slf4j
public class HttpHeaderModificationConfig implements Filter {
private static final String HEADER_DEMO_NAME = "name";
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest req = (HttpServletRequest) request;
// modify HTTP Request Header
final HttpServletRequestWrapper reqWrapper = new HttpServletRequestWrapper(req) {
#Override
public String getHeader(String name) {
if (HEADER_DEMO_NAME.equals(name)) {
return "Changed";
}
return super.getHeader(name);
}
};
log.info("After Changed with Name {}", reqWrapper.getHeader(HEADER_DEMO_NAME));
chain.doFilter(reqWrapper, response);
}
}
One option i can think of using Filters i.e. create a Filter to check for the header userId and trim it if its present or provide it a default value if its not present and store it in another header lets say customUserId. In your controller you can comfortably use your custom headers to be sure of that it will be valid as per your requirements. You can refer to the below article for creating a generic bean
https://www.baeldung.com/spring-boot-add-filter

Spring HttpServletRequest unaccessible in HystrixCommand

Inside a Javanica annotated #HystrixCommand we are checking if the request was in an actual HTTP servlet request by checking:
RequestContextHolder.getRequestAttributes() != null;
However invoked from a #HystrixCommand this condition is always false, even if the request came from a Spring MVC request.
If I remove the #HystrixCommand annotation everything works fine.
We also tried to use the HttpServletRequest directly, this works fine (without #HystrixCommand):
LOGGER.info(request.getHeader("X-Client"));
With annotated #HystrixCommand we are facing exception indicating I am not in an valid HttpServletRequest. I know it is due to Hystrix running commands in separate Threads from its own ThreadPool and tried to do this, but doesn't work either:
public class RequestServletFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
// No Impl
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
chain.doFilter(request, response);
} finally {
context.shutdown();
}
}
#Override
public void destroy() {
// No Impl
}
Does someone have a clue how to delegate the Spring HttpServletRequest into HystrixCommands?
Any help is appreciated.
When using the RequestContextHolder by default it parameters are not shared (for good reasons!).
Assuming that you are using a DispatcherServlet to handle your request you can set its [threadContextInheritable] to true to have the RequestContext and LocaleContext shared between requests.
The same applies for the RequestContextFilter, it isn't possible with the RequestContextListener.
Note: I would consider sharing the HttpServletRequest between threads as something you shouldn't be doing and should be done with great care!

Resources