Where to clear the requestId from MDC - java

I'm trying to add a requestId to my web app's logs, as shown here.
public class MDCFilter implements ContainerRequestFilter, ContainerResponseFilter
{
private static final String CLIENT_ID = "client-id";
#Context
protected HttpServletRequest r;
#Override
public void filter(ContainerRequestContext req) throws IOException
{
Optional<String> clientId = Optional.fromNullable(r.getHeader("X-Forwarded-For"));
MDC.put(CLIENT_ID, clientId.or(defaultClientId()));
}
#Override
public void filter(ContainerRequestContext req, ContainerResponseContext resp) throws IOException
{
MDC.remove(CLIENT_ID);
}
private String defaultClientId()
{
return "Direct:" + r.getRemoteAddr();
}
}
The problem with this is that it sets and removes requestId from MDC in the above filter. I also log the bodies of the responses my application generates with a WriterInterceptor. The problem is that, since interceptors run after filters, by the time my WriterInterceptor executes, there is no requestId in MDC.
My question is, is it ok to call MDC.clear() at the end of WriterInterceptor (which feels somehow hacky), or is there a better way of achieving this?

I'm having the same problem. This is how it currently works in code I'm developing
#Order(Ordered.HIGHEST_PRECEDENCE)
public final class RequestFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
//stuff
chain.doFilter(request, response);
} finally {
// this clears a specific entry from the MDC but not the entire MDC
RequestContext.clear();
}
}
}
I could clear the entire MDC in the finally block here...but I don't know when Spring logs exceptions. Would I be clearing too early? My new plan is to clear the MDC when I start a request.
I'm not sure if this is a full answer, but clearing the MDC in my highest precedence filter should work.

Related

Spring secure filter to protect anonymous requests

One more question about spring configuration...
I have several rest methods opened to everyone and not secured. These rest methods on server #1 are used by another server #2 in the same domain to get some data. The idea is that server #2 sets my_super_secure_cookie to some secure token and server #1 decodes and verifies it. Here is the code:
#Configuration
class SecurityConfig extends WebSecurityConfigurerAdapter {
// Some code
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/rest/public/*").permitAll()
.anyRequest().authenticated();
}
// More code
}
public class SuperSecurityFilter extends FilterSecurityInterceptor implements Filter {
public SuperSecurityFilter(String key) {
super(key);
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
Cookie[] cookies = req.getCookies();
Optional<Cookie> tokenCookie = Arrays.stream(cookies).filter(cookie -> cookie.getName().equals("my_super_secure_cookie")).findFirst();
if (tokenCookie.isPresent()) {
Cookie cookie = tokenCookie.get();
TokenCookie.create(cookie.getValue()).validate();
} else {
throw new Exception("Ooops!"));
}
chain.doFilter(req, res);
}
}
The question is how do I configure SecurityConfig to use SecurityTokenFilter on request to any of the /rest/public/* rest methods. Something like:
http
.antMatcher("/rest/public/*")
.addFilterBefore(new SuperSecurityFilter());
is not working, SuperSecurityFilter is not called on request.
p.s. I'm forced to work with this type of security model due to current business logic restrictions.
I solved (applied workaround?) the issue I have by implementing not filter, but interceptor, like this:
public class SuperSecurityInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// implementation here
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// Nothing here
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// Nothing here
}
}
And registered this interceptor in my entity extending WebMvcConfigurerAdapter. Like this:
registry.addInterceptor(new SupeSecurityInterceptor()).addPathPatterns("/rest/public/*");
Not sure if this is right thing to do though... Anyway would be glad to know about the conventional approach of implementing this type of functionality.

Run code on http context before or after spring boot controller

I'd like to run some code (for logging, or custom security, etc) before and/or after spring calls the controller method. I know Filters can be created to operate on ServletRequests and ServletResponses, but it's not clear how to access headers, body, query parameters, etc from those.
Most similar to what I'm trying to do is action composition in Play Framework for java.
Is there a way to do this in Spring?
Here is an example of how to inject a Header for every request using a Filter
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public final class VersionFilter implements Filter {
#Value("${version}")
protected String version;
protected FilterConfig filterConfig;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse responseAddition = (HttpServletResponse) response;
responseAddition.addHeader("X-Application-Version", this.version);
chain.doFilter(request,responseAddition);
}
#Override
public void destroy() {
}
}

Custom filter extending UrlRewriteFilter to load rules from database

I have custom redirection filters in my project, which are loading defined rules from database, and handle redirections.
In addition to those, I use also UrlRewriteFilter , with some rules defined in urlrewrite.xml.
Now I want to switch completely to use tuckey's filter, however I want to load rules from my database, instead of having them defined in xml (so that I can have single place to keep redirection rules).
My idea is to extend UrlRewriteFilter, and instead of initializing rules from XML files, load my rules from database.
Here is what I have so far,
#Service
public class CustomUrlRewriteFilter extends UrlRewriteFilter {
#Autowired
private RedirectService redirectService;
private UrlRewriter urlRewriter;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
super.init(filterConfig);
}
#Override
protected UrlRewriter getUrlRewriter(ServletRequest request, ServletResponse response, FilterChain chain) {
return urlRewriter;
}
#PostConstruct
public void initUrlRewriter() {
List<Redirect> redirects = redirectService.retrieveAll();
Conf conf = new Conf();
for (Redirect redirect : redirects) {
NormalRule rule = new NormalRule();
rule.setMatchType(redirect.getMatchType());
rule.setToType(redirect.getRedirectType());
rule.setFrom(redirect.getPath());
rule.setTo(redirect.getTarget());
rule.setQueryStringAppend("true");
conf.addRule(rule);
}
conf.setUseQueryString(true);
urlRewriter = new UrlRewriter(conf);
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
ServletException {
super.doFilter(req, resp, chain);
}
}
The problem is, redirection is not happening, I do not get any exception. When I debug, I can see that when processing request it returns false, as it can't find a rule chain (could it be that I need to init somehow rule chain?).
I assume I miss something to override, or my initialization is wrong. Does anyone has an experience on this? Is it possible to achieve proper behavior at all?
Any help is appreciated.
So finally I could manage to get this work.
The problem was that adding rules to the Conf is not enough, I had to also initilize those rules by calling conf.initialise().
On the other side, conf.initialise() itself calls NormalRule.initialise() which is checking the existence and validity of urlrewrite.xml, and if it does not properly initilized it returns false and the configuration is not initilizing properly.
The workaround is to provide custom rule extending NormalRule.initialise(), which explicitly returns always true when calling it's initialise().
Here is the example of custom rule
public final class CustomRule extends NormalRule {
public CustomRule(RedirectType redirectType, MatchType matchType, String from, String to) {
super();
setMatchType(matchType);
setToType(redirectType);
setFrom(from);
setTo(to);
setQueryStringAppend("true");
}
#Override
public boolean initialise(ServletContext context) {
super.initialise(context);
return true;
}
}
And then when creating UrlRewriter I need to initiliaze the configuration, and that's it.
#Service
public class CustomUrlRewriteFilter extends UrlRewriteFilter {
#Autowired
private RedirectService redirectService;
private UrlRewriter urlRewriter;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
super.init(filterConfig);
}
#Override
protected UrlRewriter getUrlRewriter(ServletRequest request, ServletResponse response, FilterChain chain) {
return urlRewriter;
}
#PostConstruct
public void initUrlRewriter() {
List<Redirect> redirects = redirectService.retrieveAll();
Conf conf = new Conf();
for (Redirect redirect : redirects) {
CustomRule rule = new CustomRule(
redirect.getRedirectType(), redirect.getMatchType(), redirect.getPath(), redirect.getTarget()
);
conf.addRule(rule);
}
conf.setUseQueryString(true);
conf.initialise();
urlRewriter = new UrlRewriter(conf);
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
ServletException {
super.doFilter(req, resp, chain);
}
#Override
public void destroy() {
super.destroy();
}
}
Still: Comments are welcomed.
In my case, I wanted to use a property value to build a rule. I found a shorter version to achieve the same as the above post.
#Component
public class CustomUrlRewriteFilter extends UrlRewriteFilter {
#Value("${sites.cities.codes}")
private String citiesCodes;
#Override
protected void loadUrlRewriter(FilterConfig filterConfig) throws ServletException {
Conf conf = new Conf();
NormalRule rule = new NormalRule();
rule.setMatchType("regex");
rule.setName("Remove City param");
String from = "^/(" + citiesCodes + ")/?(.*)$";
rule.setFrom(from);
rule.setTo("/$2");
conf.addRule(rule);
conf.initialise();
checkConf(conf);
}
}

Is there a way to check if I'm inside a servlet request with Guice?

I'm writing a JUL logging Handler and I'd like to augment the logged messages with information about the current request, if we're currently handling a request. To accomplish this, I've injected a Provider<Thing> into the Handler, where Thing is #RequestScoped.
However, calling provider.get() throws an OutOfScopeException if the logging happens while we're not handling a request. I feel like catching the OutOfScopeException would be bad form. Is there a better way to determine whether or not a request is currently executing?
With wicket I used a little trick. This should be framework independent. I made a request filter and put a public static ThreadLocal in it. So if current thread is born from request, threadlocal will be set.
public class SessionContext implements Filter {
private static final ThreadLocal<HttpSession> session = new ThreadLocal<HttpSession>();
#Override
public void init(FilterConfig filterConfig) throws ServletException {
return;
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
session.set(((HttpServletRequest)servletRequest).getSession());
filterChain.doFilter(servletRequest, servletResponse);
}
#Override
public void destroy() {
return;
}
public static HttpSession getSession(){
return session.get();
}
public static User getUser(){
return (User) session.get().getAttribute(UserService.USER);
}
}
and in web.xml:
<filter>
<filter-name>session</filter-name>
<filter-class>SessionContext</filter-class>
</filter>
As far as I know, there is no elegant way to do this. The Guice API is tight and will not grant access to the thread-local needed to make such a test. As of version 3, the thread-local in question sits on com.google.inject.servlet.GuiceFilter#localContext. You could access it by reflection but it is arguably even worse style than catching the exception.
I would stick on caching the exception... or hack into that class and add an static boolean test method.

can we change servlet filter with in-it parameters as interceptor in spring

I have a filter in one of the jar files added as reference.
My project is in spring nature. I have developed a web services. All the requests to this web services should be intercepted by my filter "HelloWorld".
This filter is there in one of the reference files.
Here I thought I would implement it as Interceptor.
The filter in reference files looks like
public class HelloWorld implements Filter {
private static final Logger _logger = Logger.getLogger(HelloWorld.class.getName());
protected String name = null;
public HelloWorld()
{
}
public HelloWorld(String name) {
this.name = name;
}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
PrintWriter out = response.getWriter();
out.println("Hello "+name);
chain.doFilter(req, res);
}
public void init(FilterConfig config) throws ServletException {
//Get init parameter
String testParam = config.getInitParameter("test");
//Print the init parameter
System.out.println("test param: " + testParam);
}
public void destroy() {
//add code to release any resource
}
//some other methods as well
}
What would be the best way to implement this. I cannot configure filter in my web.xml due to limitation in my application.
Can we directly give this HelloWorld filter as reference to interceptor, so that it behaves like interceptor.
Can we change this filter as interceptor in spring and configure in spring.xml without changing the functionality.
Apologies if my question is simple.
Thanks.

Categories

Resources