Spring Boot:注入自定义上下文路径 [英] Spring Boot: Inject a custom context path

查看:38
本文介绍了Spring Boot:注入自定义上下文路径的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在运行带有嵌入式 Tomcat 的 Spring Boot 1.2.3 应用程序.

I'm running a Spring Boot 1.2.3 application with embedded Tomcat.

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

I'd like to inject a custom contextPath on every request, based on the first part of the URL.

示例:

  1. http://localhost:8080/foo 默认有 contextPath="" 并且应该得到 contextPath="foo"

  1. http://localhost:8080/foo has by default contextPath="" and should get contextPath="foo"

http://localhost:8080/foo/bar 默认有 contextPath="" 并且应该得到 contextPath="foo"

http://localhost:8080/foo/bar has by default contextPath="" and should get contextPath="foo"

(没有路径的 URL 应保持原样)

(URLs without path should stay as is)

我尝试使用 @Order(Ordered.HIGHEST_PRECEDENCE) 编写自定义 javax.servlet.Filter,但似乎我遗漏了一些东西.代码如下:

I tried to write a custom javax.servlet.Filter with @Order(Ordered.HIGHEST_PRECEDENCE), but it seems like I'm missing 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() {}
}

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

This should simply take the String after the first / and before (if any) the second / and then use it as return value for getContextPath().

但是 Spring @Controller @RequestMapping 和 Spring Security 的 antMatchers("/") 似乎并不尊重它.两者仍然像 contextPath="" 一样工作.

But Spring @Controller @RequestMapping and Spring Security's antMatchers("/") does not seem to respect it. Both still work as if contextPath="".

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

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

推荐答案

成功了!

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

Spring Security docs ( http://docs.spring.io/spring-security/site/docs/3.1.x/reference/security-filter-chain.html ) say: "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."

所以我只是覆盖了 servletPathcontextPath(即使它没有被 Spring Security 使用).另外我添加了一些小的重定向,因为通常在点击 http://localhost:8080/myContext 时你会被重定向到 http://localhost:8080/myContext/ 和 Spring证券蚂蚁匹配器不喜欢缺少的尾随斜线.

So I just did override servletPath and contextPath (even if it's not used by Spring Security). Additionally I added some small redirect, because normally when hitting http://localhost:8080/myContext you get redirected to http://localhost:8080/myContext/ and Spring Securities Ant Matcher did not like the missing trailing slash.

所以这是我的 MultiTenancyFilter 代码:

So here's my MultiTenancyFilter code:

@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/

It simply extracts the contextPath and servletPath using the URL schema mentioned here: https://theholyjava.wordpress.com/2014/03/24/httpservletrequest-requesturirequesturlcontextpathservletpathpathinfoquerystring/

此外,我必须提供一个自定义的 getAlreadyFilteredAttributeName 方法,否则过滤器会被调用两次.(这导致剥离 contextPath 两次)

Additionally I had to provide a custom getAlreadyFilteredAttributeName method, because else the filter got called twice. (This resulted in stripping the contextPath twice)

这篇关于Spring Boot:注入自定义上下文路径的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆