Spring 引导:注入自定义上下文路径
Spring Boot: Inject a custom context path
我是 运行 一个 Spring 带有嵌入式 Tomcat 的 Boot 1.2.3 应用程序。
我想根据 URL.
的第一部分,为每个请求注入自定义 contextPath
示例:
http://localhost:8080/foo
默认有 contextPath=""
应该得到 contextPath="foo"
http://localhost:8080/foo/bar
默认有 contextPath=""
应该得到 contextPath="foo"
(URL没有路径的应该保持原样)
我试图用 @Order(Ordered.HIGHEST_PRECEDENCE)
编写一个自定义 javax.servlet.Filter
,但我似乎遗漏了什么。这是代码:
@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()
的 return 值。
但是Spring @Controller @RequestMapping 和Spring 安全的antMatchers("/")
似乎并不尊重它。两者仍然像 contextPath=""
.
一样工作
如何动态覆盖每个请求的上下文路径?
成功了!
Spring 安全文档 (http://docs.spring.io/spring-security/site/docs/3.1.x/reference/security-filter-chain.html) 说:"Spring Security is only interested in securing paths within the application, so the contextPath is ignored. Unfortunately, the servlet spec does not define exactly what the values of servletPath and pathInfo will contain for a particular request URI. [...] The strategy is implemented in the class AntPathRequestMatcher which uses Spring's AntPathMatcher to perform a case-insensitive match of the pattern against the concatenated servletPath and pathInfo, ignoring the queryString."
所以我只是覆盖了 servletPath
和 contextPath
(即使它没有被 Spring 安全部门使用)。此外,我添加了一些小的重定向,因为通常在点击 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
两次)
我是 运行 一个 Spring 带有嵌入式 Tomcat 的 Boot 1.2.3 应用程序。
我想根据 URL.
的第一部分,为每个请求注入自定义 contextPath示例:
http://localhost:8080/foo
默认有contextPath=""
应该得到contextPath="foo"
http://localhost:8080/foo/bar
默认有contextPath=""
应该得到contextPath="foo"
(URL没有路径的应该保持原样)
我试图用 @Order(Ordered.HIGHEST_PRECEDENCE)
编写一个自定义 javax.servlet.Filter
,但我似乎遗漏了什么。这是代码:
@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()
的 return 值。
但是Spring @Controller @RequestMapping 和Spring 安全的antMatchers("/")
似乎并不尊重它。两者仍然像 contextPath=""
.
如何动态覆盖每个请求的上下文路径?
成功了!
Spring 安全文档 (http://docs.spring.io/spring-security/site/docs/3.1.x/reference/security-filter-chain.html) 说:"Spring Security is only interested in securing paths within the application, so the contextPath is ignored. Unfortunately, the servlet spec does not define exactly what the values of servletPath and pathInfo will contain for a particular request URI. [...] The strategy is implemented in the class AntPathRequestMatcher which uses Spring's AntPathMatcher to perform a case-insensitive match of the pattern against the concatenated servletPath and pathInfo, ignoring the queryString."
所以我只是覆盖了 servletPath
和 contextPath
(即使它没有被 Spring 安全部门使用)。此外,我添加了一些小的重定向,因为通常在点击 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
两次)