Статьи

Как исключить URL из фильтра

По умолчанию фильтры не поддерживают исключение определенного шаблона URL-адреса: всякий раз, когда вы определяете шаблон URL-адреса для фильтра, любой запрос, соответствующий этому шаблону, обрабатывается фильтром без исключений.

Самый простой способ исключения URL-адресов из фильтра — это сопоставить фильтр с очень специфическим шаблоном. Это выполнимо, когда это делается на ранних стадиях разработки, однако это может быть трудоемким процессом, если вы изменяете шаблон URL существующего фильтра в производственной среде, так как вам нужно переназначить все существующие URL-адреса сервлетов для достижения вашей цели.

В этом руководстве мы покажем, как программно добавить функцию исключения в существующий фильтр.

1- Пользовательский фильтр

Пользовательский фильтр — это фильтр, которым вы можете управлять. т.е. у вас есть все права на изменение его исходного кода.

Предположим, у нас есть существующее веб-приложение, которое аутентифицирует запросы пользователей через LDAP. Все запросы сервлета проходят через LDAPAuthenticationFilter, который отображается в / * следующим образом:

1
2
3
4
5
6
7
8
<filter>
    <filter-name>LDAPAuthenticationFilter</filter-name>
    <filter-class>com.programmer.gate.filters.LDAPAuthenticationFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>LDAPAuthenticationFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Наш фильтр просто аутентифицирует запрос и впоследствии вызывает chain.doFilter () :

LDAPAuthenticationFilter.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.programmer.gate.filters;
  
import java.io.IOException;
  
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
  
public class LDAPAuthenticationFilter implements Filter{
  
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {
  
        // Authenticate the request through LDAP
        System.out.println("Authenticating the request through LDAP");
         
        // Forward the request to the next filter or servlet in the chain.
        chain.doFilter(req, resp);
    }
     
    public void init(FilterConfig filterConfig) throws ServletException {
    }
     
    public void destroy() {
        // TODO Auto-generated method stub
    }
}

Теперь предположим, что мы хотим создать сервлет, который требует простой аутентификации базы данных и не должен проходить через LDAP. Первым делом мы думаем об этом, чтобы создать новый фильтр и сопоставить его с конкретным шаблоном URL нового сервлета.

Поэтому мы создаем новый фильтр с именем DatabaseAuthenticationFilter, который просто аутентифицирует запрос через базу данных и впоследствии вызывает chain.doFilter () :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.programmer.gate.filters;
  
import java.io.IOException;
  
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
  
public class DatabaseAuthenticationFilter implements Filter{
  
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {
  
        // Authenticate the request through database then forward the request to the next filter or servlet in the chain
        System.out.println("Authenticating the request through database");
         
        chain.doFilter(req, resp);
    }
     
    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub
         
    }
     
    public void destroy() {
        // TODO Auto-generated method stub
         
    }
}

Мы определяем наш фильтр в файле web.xml для обработки только определенных URL-адресов, начинающихся с / DatabaseAuthenticatedServlet :

1
2
3
4
5
6
7
8
<filter>
    <filter-name>DatabaseAuthenticationFilter</filter-name>
    <filter-class>com.programmer.gate.filters.DatabaseAuthenticationFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>DatabaseAuthenticationFilter</filter-name>
    <url-pattern>/DatabaseAuthenticatedServlet/*</url-pattern>
</filter-mapping>

Проблема здесь в том, что запросы типа / DatabaseAuthenticatedServlet также будут соответствовать шаблону корневого URL «/ *», т.е. наш запрос будет проходить через 2 процесса аутентификации: LDAP и Database, порядок зависит от того, какой фильтр определен первым в web.xml .

Чтобы решить эту проблему, нам нужно изменить LDAPAuthenticationFilter, чтобы он исключал URL-адреса, начинающиеся с / DatabaseAuthenticatedServlet. Обычно люди статически проверяют URL-адрес сервлета запроса внутри метода doFilter () и просто обходят процесс аутентификации при его обнаружении.

Здесь мы делаем еще один шаг и внедряем более динамичное решение, которое позволяет нам управлять исключенными URL-адресами через web.xml .

Ниже приведены шаги для добавления функции исключения в LDAPAuthenticationFilter :

  • Добавьте новое поле с именем exclusiveUrls типа List <String> :
    1
    private List excludedUrls;
  • Внутри метода init () считайте атрибут конфигурации с именем selectedUrls, используя FilterConfig, атрибут должен разделяться запятыми, чтобы мы исключали столько URL-адресов, сколько нам нужно.
    1
    2
    3
    4
    public void init(FilterConfig filterConfig) throws ServletException {
        String excludePattern = filterConfig.getInitParameter("excludedUrls");
        excludedUrls = Arrays.asList(excludePattern.split(","));
    }
  • Измените doFilter () , чтобы проверить, принадлежит ли запрашиваемый URL-адрес списку предварительно определенных исключенных URL-адресов, если это так, то просто перенаправьте запрос следующему фильтру или сервлету в цепочке, иначе сделайте логику аутентификации.
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
                throws IOException, ServletException {
      
        String path = ((HttpServletRequest) req).getServletPath();
             
        if(!excludedUrls.contains(path))
        {
            // Authenticate the request through LDAP
            System.out.println("Authenticating the request through LDAP");
        }
             
        // Forward the request to the next filter or servlet in the chain.
        chain.doFilter(req, resp);
    }
  • Теперь внутри web.xml вы можете контролировать, какой URL исключать из аутентификации LDAP без единого изменения кода:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <filter>
        <filter-name>LDAPAuthenticationFilter</filter-name>
        <filter-class>com.programmer.gate.filters.LDAPAuthenticationFilter</filter-class>
        <init-param>
            <param-name>excludedUrls</param-name>
            <!-- Comma separated list of excluded servlets  -->
            <param-value>/DatabaseAuthenticatedServlet,/UnAuthenticatedServlet</param-value>
        </init-param>
    </filter>

Вот так выглядит LDAPAuthenticationFilter после добавления функции исключения:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.programmer.gate.filters;
  
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
  
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
  
public class LDAPAuthenticationFilter implements Filter{
     
    private List excludedUrls;
  
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {
  
        String path = ((HttpServletRequest) req).getServletPath();
         
        if(!excludedUrls.contains(path))
        {
            // Authenticate the request through LDAP
            System.out.println("Authenticating the request through LDAP");
        }
         
        // Forward the request to the next filter or servlet in the chain.
        chain.doFilter(req, resp);
    }
     
    public void init(FilterConfig filterConfig) throws ServletException {
        String excludePattern = filterConfig.getInitParameter("excludedUrls");
        excludedUrls = Arrays.asList(excludePattern.split(","));
    }
     
    public void destroy() {
        // TODO Auto-generated method stub
    }
}

2- Сторонний фильтр

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

В этом разделе мы немного изменим наш пример и используем аутентификацию CAS вместо LDAP. Вот как мы определяем наш фильтр аутентификации CAS в web.xml :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<filter>
  <filter-name>CAS Authentication Filter</filter-name>
  <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
  <init-param>
    <param-name>casServerLoginUrl</param-name>
    <param-value>https://localhost:8443/cas/login</param-value>
  </init-param>
  <init-param>
    <param-name>serverName</param-name>
    <param-value>localhost</param-value>
  </init-param>
</filter>
<filter-mapping>
    <filter-name>CAS Authentication Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Аутентификация CAS выполняется через стороннюю библиотеку, теперь для поддержки аутентификации базы данных мы не можем изменять исходный код CAS, как мы это делали в предыдущем примере с LDAP.

Решение для исключения URL-адресов из стороннего фильтра состоит в том, чтобы обернуть его новым настраиваемым фильтром, который просто добавляет функцию исключения и делегирует логику фильтра для обернутого класса.

Ниже приведены шаги для добавления функции исключения из к аутентификации CAS:

  • Создайте новый фильтр с именем CASCustomAuthenticationFilter следующим образом:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class CASCustomAuthenticationFilter implements Filter{
         
        private AuthenticationFilter casAuthenticationFilter = new AuthenticationFilter();
        private List excludedUrls;
      
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
                throws IOException, ServletException {
      
            String path = ((HttpServletRequest) req).getServletPath();
             
            if(!excludedUrls.contains(path))
            {
                // Authenticate the request through CAS
                casAuthenticationFilter.doFilter(req,resp,chain);
            }
             
            // Forward the request to the next filter or servlet in the chain.
            chain.doFilter(req, resp);
        }
         
        public void init(FilterConfig arg0) throws ServletException {
      
            String excludePattern = filterConfig.getInitParameter("excludedUrls");
            excludedUrls = Arrays.asList(excludePattern.split(","));
             
            casAuthenticationFilter.init();
        }
         
        public void destroy() {
            casAuthenticationFilter.destroy();
        }
    }

    Наш специальный фильтр оборачивает фильтр аутентификации CAS через композицию, его основная цель — просто управлять тем, какие URL-адреса должны быть аутентифицированы через CAS, в то время как мы не касались процедуры аутентификации CAS.

  • В файле web.xml мы изменили определение фильтра, чтобы использовать CASCustomAuthenticationFilter вместо реализации CAS по умолчанию:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <filter>
      <filter-name>CAS Authentication Filter</filter-name>
      <filter-class>com.programmer.gate.filters.CASCustomAuthenticationFilter</filter-class>
      <init-param>
        <param-name>casServerLoginUrl</param-name>
        <param-value>https:localhost:8443/cas/login</param-value>
      </init-param>
      <init-param>
        <param-name>serverName</param-name>
        <param-value>localhost</param-value>
      </init-param>
      <init-param>
        <param-name>excludeUrls</param-name>
        <param-value>/DatabaseAuthenticatedServlet</param-value>
      </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CAS Authentication Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Вот и все, пожалуйста, оставьте свои мысли в разделе комментариев ниже.

Опубликовано на Java Code Geeks с разрешения Хуссейна Терека, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Как исключить URL из фильтра

Мнения, высказанные участниками Java Code Geeks, являются их собственными.