Home > Software engineering >  Spring filter: obtain servlet url-pattern
Spring filter: obtain servlet url-pattern

Time:03-26

Imagine I have defined these REST endpoints:

@RequestMapping(path = "/user")
@RestController
public class ConfigUserController {

    [...]
    
    @GetMapping(path = "/variables/")
    public ResponseEntity<List<Variable>> getAllVariables(...)
        [...]
    }
    
    @GetMapping(path = "/variables/{name}")
    public ResponseEntity<Variable> getVariableByName(...)
        [...]
    }
    
    @PutMapping(path = "/variables/{name}/{value}")
    public ResponseEntity<Variable> setVariableByName(...)
        [...]
    }
}

I've defined two filter (logging and authorization), inside the filters I want to get the url-patter which matched current request. Using the example above:

  • if the request was a GET to /variables, I want "/variables"
  • if the request was a GET to /variables/myfancyname, I want "/variables/{name}"
  • if the request was a PUT to /variables/myfancyname/myvalue, I want "/variables/{name}/{value}"

Basically what I need is to get the path defined in the mapping annotation inside the doFilter(ServletRequest request, ServletResponse response, FilterChain chain) method of my filters.

Ideally I want to have do the possibility to access the url-pattern value in my filter like this:

@Component
@Order(3)
public class MyAuthorizationRequestFilter extends GenericFilterBean {
    [...]
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // here I get the value
        String urlPattern = request.useSomeMethod().toGetUrlPattern();
        [...]
        boolean auth = false;
        // here I do stuff
        [...]
        // finally let's see if the user is authorized
        if (auth) {
            chain.doFilter(request, responde);
        } else {
            throw SomeAuthorizationError();
        }
    }
    [...]
}

I've been searching but I wasn't able to find a working solution.

If it's possible I prefer not to scan for collections or web.xml files (which I don't have), nor using reflections, because the code will be executed every time the filter get triggered and I don't want to affect performance.

Links or suggestions are welcome.

EDIT: added details, addes filter example cose

CodePudding user response:

Assuming that you are using spring web here. You can define an HandlerInterceptor and register it with InterceptorRegistry.

@Component
public class LogPathTemplateInterceptor
        implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
        String pathTemplate = (String)request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        System.out.println("Incoming Request path Template : "   pathTemplate);
        return true;
    }
}

After defining this, register it with InterceptorRegistry. See how the path pattern added to the interceptor.

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Autowired
    private LogPathTemplateInterceptor logInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(logInterceptor)
                .addPathPatterns("/variables/**");
    }
}

This should log the request template path instead of path with variables.

CodePudding user response:

Here is a solution using Servlet filter

Your Controller

@RequestMapping(path = "/user")
@RestController
public class ConfigUserController {

    @GetMapping(path = "/variables/")
    public ResponseEntity<List<Variable>> getAllVariables() {
        return null;
    }

    @GetMapping(path = "/variables/{name}")
    public ResponseEntity<Variable> getVariableByName(@PathVariable("name") String name) {
        return new ResponseEntity<Variable>(new Variable(name), HttpStatus.OK);
    }

    @PutMapping(path = "/variables/{name}/{value}")
    public ResponseEntity<Variable> setVariableByName() {
        return null;
    }
}

Custom Filter

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerMapping;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        chain.doFilter(request, response);

        String pattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        System.out.println("Request template is, "   pattern);

    }

O/P:

 Request template is, /user/variables/{name}

Note* Do the chain.doFilter first before trying to do the call to getAttribute

More Information -

https://boudhayan-dev.medium.com/demystifying-spring-security-setup-e0491acc7df7

https://www.javadevjournal.com/spring-security/spring-security-filters/

https://www.javadevjournal.com/spring-security/custom-filter-in-spring-security/

  • Related