Как преобразовать поддомен в путь с помощью Embedded Tomcat 8 и Spring boot

Как переписать поддомены на пути?

Пример:

  • foo.bar .example.com --> example.com /foo/bar

Или лучше было бы (обратные папки):

  • foo.bar .example.com --> example.com /bar/foo

Запрос foo.bar .example.com должен отправить файл в /src/main/resources/static/ bar/foo /index.html.

В Apache2 это делается с помощью mod_rewrite. Я нашел документацию о перезаписи с помощью Tomcat 8, но вопрос в том, куда поместить эти файлы с использованием весенней загрузки?


Обновить

Я попытался использовать UrlRewriteFilter, но, похоже, невозможно определить правила в пути домена с помощью подстановки регулярных выражений.

Это моя конфигурация:

Зависимость Мейвена:

<dependency>
    <groupId>org.tuckey</groupId>
    <artifactId>urlrewritefilter</artifactId>
    <version>4.0.3</version>
</dependency>

Spring Java Config для регистрации фильтра сервлетов:

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application
{
    public static void main(String[] args)
    {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean()
    {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();

        registrationBean.setFilter(new UrlRewriteFilter());
        registrationBean.addUrlPatterns("*");
        registrationBean.addInitParameter("confReloadCheckInterval", "5");
        registrationBean.addInitParameter("logLevel", "DEBUG");

        return registrationBean;
    }
}

urlrewrite.xml в /src/main/webapp/WEB-INF

<?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE urlrewrite
    PUBLIC "-//tuckey.org//DTD UrlRewrite 4.0//EN"
    "http://www.tuckey.org/res/dtds/urlrewrite4.0.dtd">

<urlrewrite>
    <rule>
        <name>Translate</name>
        <condition name="host" operator="equal">foo.bar.example.com</condition>
        <from>^(.*)</from>
        <to type="redirect">example.com/bar/foo</to>
    </rule>
</urlrewrite>

С этим жестко закодированным доменом это работает, но оно должно работать для каждого субдомена, подобного этому.


person d0x    schedule 05.12.2014    source источник


Ответы (4)


Создайте свой собственный фильтр.

Этот фильтр должен:

  • проверьте, нужно ли вам переписать запрос
  • Если да, перепишите URL и URI.
  • переслать запрос
  • он приходит снова через тот же фильтр, но первая проверка выдаст false
  • не забудьте и будьте осторожны позвонить chain.doFilter

Переадресация не изменит никаких URL-адресов в браузере. Просто отправьте содержимое файла.

Следующий код может быть реализацией такого фильтра. Это не какой-то чистый код. Просто быстрый и грязный рабочий код:

@Component
public class SubdomainToReversePathFilter implements Filter {
    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest req = (HttpServletRequest) request;
        final String requestURI = req.getRequestURI();

        if (!requestURI.endsWith("/")) {
            chain.doFilter(request, response);
        } else {
            final String servername = req.getServerName();
            final Domain domain = getDomain(servername);

            if (domain.hasSubdomain()) {
                final HttpServletRequestWrapper wrapped = wrapServerName(req, domain);
                wrapped.getRequestDispatcher(requestURI + domain.getSubdomainAsPath()).forward(wrapped, response);
            } else {
                chain.doFilter(request, response);
            }
        }
    }

    private Domain getDomain(final String domain) {
        final String[] domainParts = domain.split("\\.");
        String mainDomain;
        String subDomain = null;

        final int dpLength = domainParts.length;
        if (dpLength > 2) {
            mainDomain = domainParts[dpLength - 2] + "." + domainParts[dpLength - 1];

            subDomain = reverseDomain(domainParts);
        } else {
            mainDomain = domain;
        }

        return new Domain(mainDomain, subDomain);
    }
    private HttpServletRequestWrapper wrapServerName(final HttpServletRequest req, final Domain domain) {
        return new HttpServletRequestWrapper(req) {
            @Override
            public String getServerName() {
                return domain.getMaindomain();
            }
            // more changes? getRequesetURL()? ...?
        };
    }

    private String reverseDomain(final String[] domainParts) {
        final List<String> subdomainList = Arrays.stream(domainParts, 0, domainParts.length - 2)//
                .collect(Collectors.toList());

        Collections.reverse(subdomainList);

        return subdomainList.stream().collect(Collectors.joining("."));
    }

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void destroy() {
    }
}

Вот класс домена:

public static class Domain {
    private final String maindomain;
    private final String subdomain;

    public Domain(final String maindomain, final String subdomain) {
        this.maindomain = maindomain;
        this.subdomain = subdomain;
    }

    public String getMaindomain() {
        return maindomain;
    }

    public String getSubdomain() {
        return subdomain;
    }

    public boolean hasSubdomain() {
        return subdomain != null;
    }

    public String getSubdomainAsPath() {
        return "/" + subdomain.replaceAll("\\.", "/") + "/";
    }
}

И вам нужен Контроллер, который ловит все

@RestController
public class CatchAllController {

    @RequestMapping("**")
    public FileSystemResource deliver(final HttpServletRequest request) {
        final String file = ((String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE));

        return new FileSystemResource(getStaticFile(file));
    }

    private File getStaticFile(final String path) {
        try {
            // TODO handle correct
            return new File(CatchAllController.class.getResource("/static/" + path + "/index.html").toURI());
        } catch (final Exception e) {
            throw new RuntimeException("not found");
        }
    }
}

Я не уверен, что это необходимо для переопределения других методов в HttpServletRequestWrapper. Это повод для комментария.

Также вы должны обрабатывать случаи доставки файлов (не существующие, ...).

person Walery Strauch    schedule 09.12.2014

Вы можете использовать Backreferences для использования сгруппированных частей, совпадающих в вашем <condition>. Что-то вроде этого -

<condition name="host" operator="equal">(*).(*).example.com</condition>
<from>^(.*)</from>
<to type="redirect">example.com/%1/%2</to>

Конечно, вам придется изменить приведенное выше правило условия, чтобы прекратить нетерпеливое сопоставление.

Подробнее здесь: http://urlrewritefilter.googlecode.com/svn/trunk/src/doc/manual/4.0/index.html#condition

person avlesh    schedule 09.12.2014
comment
В этом случае у вас есть жестко запрограммированная структура из двух частей. Это: mr.foo.bar.example.com -> example.com/bar/foo/mr/ не получится. в любом случае проголосуйте за короткое и простое решение :) - person Walery Strauch; 09.12.2014

Другой способ решить эту проблему: сделать это в самом контроллере. На мой взгляд, это лучше, чем с фильтром, потому что:

  • фильтр ловит каждый запрос. Здесь вы можете лучше контролировать, какие запросы должны быть доставлены с помощью «шаблона поддомена». В качестве примера я выбрал /subdomain2path.
  • Вам не нужно изменять/оборачивать URI/URL и снова пересылать через цепочку фильтров.
  • Вся логика в этом контроллере

Методы getSubdomain и reverseDomain такие же, как в предыдущем ответе.

Это реализация:

@RestController
@RequestMapping("/subdomain2path")
public class Subdomain2PathController {

    @RequestMapping("/")
    public FileSystemResource deliver(final HttpServletRequest request) {
        final Domain subdomain = getSubdomain(request.getServerName());

        String file = "/";
        if (subdomain.hasSubdomain()) {
            file = subdomain.getSubdomainAsPath();
        }

        return new FileSystemResource(getStaticFile(file));
    }

    private Domain getSubdomain(final String domain) {
        final String[] domainParts = domain.split("\\.");
        String mainDomain;
        String subDomain = null;

        final int dpLength = domainParts.length;
        if (dpLength > 2) {
            mainDomain = domainParts[dpLength - 2] + "." + domainParts[dpLength - 1];

            subDomain = reverseDomain(domainParts);
        } else {
            mainDomain = domain;
        }

        return new Domain(mainDomain, subDomain);
    }

    private String reverseDomain(final String[] domainParts) {
        final List<String> subdomainList = Arrays.stream(domainParts, 0, domainParts.length - 2)//
                .collect(Collectors.toList());

        Collections.reverse(subdomainList);

        return subdomainList.stream().collect(Collectors.joining("."));
    }

    private File getStaticFile(final String path) {
        try {
            // TODO handle correct
            return new File(Subdomain2PathController.class.getResource("/static/" + path + "/index.html").toURI());
        } catch (final Exception e) {
            throw new RuntimeException("not found");
        }
    }
}

Класс домена такой же, как и в предыдущем ответе:

public static class Domain {
    private final String maindomain;
    private final String subdomain;

    public Domain(final String maindomain, final String subdomain) {
        this.maindomain = maindomain;
        this.subdomain = subdomain;
    }

    public String getMaindomain() {
        return maindomain;
    }

    public String getSubdomain() {
        return subdomain;
    }

    public boolean hasSubdomain() {
        return subdomain != null;
    }

    public String getSubdomainAsPath() {
        return "/" + subdomain.replaceAll("\\.", "/") + "/";
    }
}
person Walery Strauch    schedule 09.12.2014

Это сработало для меня. Надеюсь, это работает и для других.

Пожалуйста, используйте следующую зависимость


         <dependency>
           <groupId>org.tuckey</groupId>
           <artifactId>urlrewritefilter</artifactId>
           <version>4.0.4</version>
         </dependency>

Создан urlrewrite.xml в папке ресурсов

 <?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE urlrewrite
    PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN"
    "http://www.tuckey.org/res/dtds/urlrewrite3.0.dtd">

<urlrewrite>
<rule>
    <name>Domain Name Check</name>
    <condition name="host" operator="notequal">www.userdomain.com</condition>
    <from>^(.*)$</from>
    <to type="redirect">http://www.userdomain.com$1</to>
</rule>


Added in main ApplicationRunner.java

@Bean
public FilterRegistrationBean tuckeyRegistrationBean() {
    final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    registrationBean.setFilter(new CustomURLRewriter());
    return registrationBean;
}

И создал CustomURLRewriter

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.tuckey.web.filters.urlrewrite.Conf;
import org.tuckey.web.filters.urlrewrite.UrlRewriteFilter;
import org.tuckey.web.filters.urlrewrite.UrlRewriter;
import javax.servlet.*;
import java.io.InputStream;

public class CustomURLRewriter extends UrlRewriteFilter {
private UrlRewriter urlRewriter;

@Autowired
Environment env;

@Override
public void loadUrlRewriter(FilterConfig filterConfig) throws ServletException {
    try {
        ClassPathResource classPathResource = new ClassPathResource("urlrewrite.xml");
        InputStream inputStream = classPathResource.getInputStream();
        Conf conf1 = new Conf(filterConfig.getServletContext(), inputStream, "urlrewrite.xml", "");
        urlRewriter = new UrlRewriter(conf1);
    } catch (Exception e) {
        throw new ServletException(e);
    }
}

@Override
public UrlRewriter getUrlRewriter(ServletRequest request, ServletResponse response, FilterChain chain) {
    return urlRewriter;
}

@Override
public void destroyUrlRewriter() {
    if(urlRewriter != null)
        urlRewriter.destroy();
}
}
person gursahib.singh.sahni    schedule 06.08.2017