Spring Boot: inject custom context path

I am running a Spring Boot 1.2.3 application with Tomcat embedded.

I would like to add a custom contextPath for each request based on the first part of the url.

Examples:

  • http://localhost:8080/foo

    has a default contextPath=""

    and should getcontextPath="foo"

  • http://localhost:8080/foo/bar

    has a default contextPath=""

    and should getcontextPath="foo"

(URLs without path should stay as they are)

I tried to write a user javax.servlet.Filter

with @Order(Ordered.HIGHEST_PRECEDENCE)

, but it looks like I missed something. Here's the code:

@Component @Order(Ordered.HIGHEST_PRECEDENCE)
public class MultiTenancyFilter implements Filter {
    private final static Pattern pattern = Pattern.compile("^/(?<contextpath>[^/]+).*$");

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest req = (HttpServletRequest) request;
        final String requestURI = req.getRequestURI();

        Matcher matcher = pattern.matcher(requestURI);
        if(matcher.matches()) {
            chain.doFilter(new HttpServletRequestWrapper(req) {
                @Override
                public String getContextPath() {
                    return "/"+matcher.group("contextpath");
                }
            }, response);
        }
    }

    @Override public void init(FilterConfig filterConfig) throws ServletException {}
    @Override public void destroy() {}
}

      

It just needs to take the String after the first /

and before (if any) the second /

, and then use that as the return value for getContextPath()

.


But Spring @Controller @RequestMapping and Spring Security antMatchers("/")

doesn't seem to respect it. Both still work as contextPath=""

.


How can I dynamically override the context path for each request?

+3


source to share


1 answer


It worked!

The Spring Security Docs ( http://docs.spring.io/spring-security/site/docs/3.1.x/reference/security-filter-chain.html ) says "Spring Security is only interested in providing paths in the application, so contextpath is ignored Unfortunately, the servlet specification does not specify exactly what servletPath and pathInfo values ​​will contain for a particular request URI. [...] The strategy is implemented in the AntPathRequestMatcher class, which uses Spring's AntPathMatcher to perform case insensitive pattern matching with the concatenated servletPath and pathInfo, ignoring queryString. "

So, I just overridden servletPath

and contextPath

(even if not used by Spring Security). Also, I added a little redirect because normally when clicked http://localhost:8080/myContext

you are redirected to http://localhost:8080/myContext/

and Spring Securities Ant Matches didn't like the missing trailing slashes.

So here is my code MultiTenancyFilter

:



@Component @Order(Ordered.HIGHEST_PRECEDENCE)
public class MultiTenancyFilter extends OncePerRequestFilter {
    private final static Pattern pattern = Pattern.compile("^(?<contextPath>/[^/]+)(?<servletPath>.*)$");

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        Matcher matcher = pattern.matcher(request.getServletPath());
        if(matcher.matches()) {
            final String contextPath = matcher.group("contextPath");
            final String servletPath = matcher.group("servletPath");

            if(servletPath.trim().isEmpty()) {
                response.sendRedirect(contextPath+"/");
                return;
            }

            filterChain.doFilter(new HttpServletRequestWrapper(request) {
                @Override
                public String getContextPath() {
                    return contextPath;
                }
                @Override
                public String getServletPath() {
                    return servletPath;
                }
            }, response);
        } else {
            filterChain.doFilter(request, response);
        }
    }

    @Override
    protected String getAlreadyFilteredAttributeName() {
        return "multiTenancyFilter" + OncePerRequestFilter.ALREADY_FILTERED_SUFFIX;
    }
}

      

It just fetches the contextPath and servletPath using the url scheme I specified here: https://theholyjava.wordpress.com/2014/03/24/httpservletrequest-requesturirequesturlcontextpathservletpathpathinfoquerystring/

Also, I had to provide my own method getAlreadyFilteredAttributeName

, because otherwise the filter was called twice. (This resulted in deletion contextPath

twice)

+1


source







All Articles