我正在使用嵌入式Tomcat运行Spring Boot 1.2.3应用程序。
我想根据网址的第一部分为每个请求注入一个自定义的contextPath。
示例:
http://localhost:8080/foo
默认为contextPath=""
,应该contextPath="foo"
http://localhost:8080/foo/bar
默认为contextPath=""
,应该contextPath="foo"
(没有路径的网址应保持原样)
我尝试用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=""
一样工作。
如何动态覆盖每个请求的上下文路径?
答案 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;
所以我刚刚覆盖了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
两次)