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

4
我正在运行一个Spring Boot 1.2.3应用程序,其中包含嵌入式Tomcat。
我想在每个请求上注入自定义的contextPath,基于URL的第一部分。
例如:
1. http://localhost:8080/foo 默认情况下具有 contextPath="",应该得到 contextPath="foo" 2. http://localhost:8080/foo/bar 默认情况下具有 contextPath="",应该得到 contextPath="foo"
(不带路径的URL应保持不变)
我尝试编写了一个自定义的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的antMatchers("/")似乎不尊重它。两者仍然像contextPath = ""一样工作。


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

1个回答

3

搞定了!

Spring Security文档(http://docs.spring.io/spring-security/site/docs/3.1.x/reference/security-filter-chain.html)说:“Spring Security只关心应用程序内的路径安全,因此contextPath被忽略。不幸的是,servlet规范没有明确定义特定请求URI的servletPath和pathInfo的值。[...]该策略由AntPathRequestMatcher类实现,它使用Spring AntPathMatcher对模式与连接的servletPath和pathInfo进行大小写不敏感匹配,并忽略queryString。”

所以,我刚刚覆盖了servletPath和contextPath(即使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两次)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接