Spring Boot:注入自定义上下文路径

时间:2015-05-27 15:58:47

标签: spring spring-mvc tomcat spring-security spring-boot

我正在使用嵌入式Tomcat运行Spring Boot 1.2.3应用程序。

我想根据网址的第一部分为每个请求注入一个自定义的contextPath。

示例:

  1. http://localhost:8080/foo默认为contextPath="",应该contextPath="foo"

  2. http://localhost:8080/foo/bar默认为contextPath="",应该contextPath="foo"

  3. (没有路径的网址应保持原样)

    我尝试用javax.servlet.Filter编写自定义@Order(Ordered.HIGHEST_PRECEDENCE),但似乎我错过了一些东西。这是代码:

    @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() {}
    }
    

    这应该只是在第一个/之后和之前(如果有)第二个/之后使用字符串,然后将其用作getContextPath()的返回值。

    但Spring @Controller @RequestMapping和Spring Security&#39; antMatchers("/")似乎并不尊重它。两者仍然像contextPath=""一样工作。

    如何动态覆盖每个请求的上下文路径?

1 个答案:

答案 0 :(得分:2)

搞定了!

Spring Security文档(http://docs.spring.io/spring-security/site/docs/3.1.x/reference/security-filter-chain.html)说:&#34; Spring Security只对保护应用程序中的路径感兴趣,因此忽略了contextPath。不幸的是,servlet规范没有准确定义servletPath和pathInfo的值将包含在特定请求URI中的内容。 [...]该策略在AntPathRequestMatcher类中实现,该类使用Spring的AntPathMatcher对连接的servletPath和pathInfo执行模式的不区分大小写匹配,忽略queryString。&#34;

所以我刚刚覆盖了servletPathcontextPath(即使它没有被Spring Security使用)。此外,我添加了一些小的重定向,因为通常在点击http://localhost:8080/myContext时,您会被重定向到http://localhost:8080/myContext/而Spring Securities Ant Matcher不喜欢丢失的尾部斜线。

所以这是我的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;
    }
}

它只是使用此处提到的URL模式提取contextPath和servletPath:https://theholyjava.wordpress.com/2014/03/24/httpservletrequest-requesturirequesturlcontextpathservletpathpathinfoquerystring/

此外,我必须提供自定义getAlreadyFilteredAttributeName方法,因为过滤器被调用两次。 (这导致剥离contextPath两次)