Bisakah saya mengecualikan beberapa url beton dari <url-pattern> di dalam <filter-mapping>?

127

Saya ingin beberapa filter beton diterapkan untuk semua url kecuali untuk satu beton (yaitu untuk /*kecuali untuk /specialpath).

Apakah ada kemungkinan untuk melakukan itu?


Kode sampel:

<filter>
    <filter-name>SomeFilter</filter-name>
    <filter-class>org.somproject.AFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SomeFilter</filter-name>
    <url-pattern>/*</url-pattern>   <!-- the question is: how to modify this line?  -->
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>
Roma
sumber

Jawaban:

156

API Servlet standar tidak mendukung fasilitas ini. Anda mungkin ingin menggunakan filter penulisan ulang URL untuk ini seperti yang Tuckey's (yang mirip dengan HTTPD Apache mod_rewrite), atau menambahkan tanda centang pada doFilter()metode mendengarkan Filter pada /*.

String path = ((HttpServletRequest) request).getRequestURI();
if (path.startsWith("/specialpath/")) {
    chain.doFilter(request, response); // Just continue chain.
} else {
    // Do your business stuff here for all paths other than /specialpath.
}

Anda dapat, jika perlu, menentukan path-to-be-diabaikan sebagai init-paramfilter sehingga Anda bisa mengendalikannya di bagian web.xmlmanapun. Anda bisa mendapatkannya di filter sebagai berikut:

private String pathToBeIgnored;

public void init(FilterConfig config) {
    pathToBeIgnored = config.getInitParameter("pathToBeIgnored");
}

Jika filter merupakan bagian dari API pihak ke-3 dan karenanya Anda tidak dapat memodifikasinya, maka petakan pada yang lebih spesifik url-pattern, mis. /otherfilterpath/*Dan buat filter baru /*yang meneruskan jalur yang cocok dengan filter pihak ke-3.

String path = ((HttpServletRequest) request).getRequestURI();
if (path.startsWith("/specialpath/")) {
    chain.doFilter(request, response); // Just continue chain.
} else {
    request.getRequestDispatcher("/otherfilterpath" + path).forward(request, response);
}

Untuk menghindari filter ini akan memanggil dirinya sendiri dalam loop tak terbatas, Anda perlu membiarkannya hanya mendengarkan (mengirim) REQUESTdan filter pihak ke-3 FORWARDsaja.

Lihat juga:

BalusC
sumber
3
Masalah saya adalah bahwa filter itu bukan milik saya, itu dari pustaka komponen.
Roman
4
Ypu harus mengambil filter pustaka compnent dan memperluasnya untuk menambahkan kode yang ingin Anda gunakan untuk melakukan pengecualian.
gbtimmon
@ BalusC Jika "/ specialpath" hanya melayani sumber daya statis seperti js, css dll, apakah chain.doFilter () membuat respons lebih lambat? Apakah ada metode untuk menyajikan sumber daya secara langsung tanpa merantai filter?
BenhurCD
@BenhurCD: Saya benar-benar tidak tahu bagaimana Anda bisa sampai pada masalah kinerja ini.
BalusC
13

Saya menggunakan pendekatan yang dijelaskan oleh Eric Daugherty : Saya membuat servlet khusus yang selalu menjawab dengan kode 403 dan meletakkan pemetaannya sebelum yang umum.

Memetakan fragmen:

  <servlet>
    <servlet-name>generalServlet</servlet-name>
    <servlet-class>project.servlet.GeneralServlet</servlet-class>
  </servlet>
 <servlet>
    <servlet-name>specialServlet</servlet-name>
    <servlet-class>project.servlet.SpecialServlet</servlet-class>
 </servlet>
 <servlet-mapping>
    <servlet-name>specialServlet</servlet-name>
    <url-pattern>/resources/restricted/*</url-pattern>
 </servlet-mapping>
 <servlet-mapping>
    <servlet-name>generalServlet</servlet-name>
    <url-pattern>/resources/*</url-pattern>
 </servlet-mapping>

Dan kelas servlet:

public class SpecialServlet extends HttpServlet {
    public SpecialServlet() {
        super();
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.sendError(HttpServletResponse.SC_FORBIDDEN);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.sendError(HttpServletResponse.SC_FORBIDDEN);
    }
}
mrzasa
sumber
9

Pendekatan ini berfungsi saat Anda ingin mencegah filter tertentu dan semua yang berikut. Ini harus bekerja dengan baik jika Anda mis. ingin menyajikan beberapa konten sebagai sumber daya statis dalam wadah servlet Anda alih-alih membiarkan logika aplikasi Anda (melalui filter seperti GuiceFilter):

Petakan folder dengan file sumber daya statis Anda ke servlet default. Buat filter servlet dan letakkan di depan GuiceFilter di web.xml Anda. Di filter yang Anda buat, Anda dapat memisahkan antara meneruskan beberapa permintaan ke GuiceFilter dan lainnya langsung ke operator. Contoh berikut ...

web.xml

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/static/*</url-pattern>
</servlet-mapping>

<filter>
    <filter-name>StaticResourceFilter</filter-name>
    <filter-class>com.project.filter.StaticResourceFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>StaticResourceFilter</filter-name>
    <url-pattern>/static/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>guiceFilter</filter-name>
    <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>guiceFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

StaticResourceFilter.class

public class StaticResourceFilter implements Filter {

    private final static Logger LOGGER = LoggerFactory.getLogger(StaticResourceFilter.class);

    private static final String RESOURCE_PATH = "/static/";
    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {
        LOGGER.info("StaticResourceFilter initialized");
    }

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response,
                         final FilterChain chain) throws IOException, ServletException {

        String path = ((HttpServletRequest) request).getServletPath();
        if (path.toLowerCase().startsWith(RESOURCE_PATH)) {
            request.getRequestDispatcher(path).forward(request, response);
        } else {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {
        LOGGER.info("StaticResourceFilter destroyed");
    }
}

Sayangnya, jika Anda hanya ingin melewatkan satu langkah dalam rantai filter sambil mempertahankan langkah-langkah berikut, ini tidak akan berhasil.

kvitso
sumber
Saya mencoba menggunakan solusi Anda, tetapi untuk file yang saya terapkan filter dan memutus rantai, saya mendapatkan kesalahan followin; Pengecualian tanpa tertangkap dilemparkan oleh filter Static Resource Filter: java.io.FileNotFoundException. Ada yang tahu kenapa?
shamaleyte
Dalam pengaturan multi-konteks, penggunaan .getRequestURI()akan rusak (menyebabkan 404 kemungkinan besar) karena .getRequestDispatcherdiselesaikan relatif terhadap jalur konteks . Jika jalur konteks Anda adalah /a, maka dalam contoh Anda permintaan URI akan /a/static, dan menggunakan getRequestDispatcher("/a/static")akan menyebabkannya untuk menyelesaikannya /a/a/static. Perbaiki: gunakan .getServletPath()sebagai ganti .getRequestURI(). Saya akan mengirimkan hasil edit untuk memperbaikinya, tetapi hanya ingin memberikan komentar FYI
Reid
3

Saya tidak berpikir Anda bisa, satu-satunya alternatif konfigurasi lain adalah untuk menghitung jalan yang ingin Anda filter, jadi alih-alih /*Anda bisa menambahkan beberapa untuk /this/*dan /that/*lain - lain, tetapi itu tidak akan mengarah ke solusi yang cukup ketika Anda memiliki banyak dari jalur tersebut.

Apa yang dapat Anda lakukan adalah menambahkan parameter ke filter yang memberikan ekspresi (seperti ekspresi reguler) yang digunakan untuk melewati fungsionalitas filter untuk jalur yang cocok. Wadah servlet masih akan memanggil filter Anda untuk url itu, tetapi Anda akan memiliki kontrol yang lebih baik atas konfigurasi.

Edit

Sekarang setelah Anda menyebutkan bahwa Anda tidak memiliki kendali atas filter, apa yang dapat Anda lakukan adalah mewarisi dari supermetode pemanggilan filter dalam metodenya kecuali ketika jalur url yang ingin Anda lewati ada dan ikuti rantai filter seperti @BalusC yang diusulkan, atau buat filter yang instantiate filter Anda dan delegasi dalam keadaan yang sama. Dalam kedua kasus, parameter filter akan mencakup parameter ekspresi yang Anda tambahkan dan orang-orang dari filter yang Anda warisi atau didelegasikan ke.

Keuntungan membangun filter pendelegasian (pembungkus) adalah Anda dapat menambahkan kelas filter dari filter terbungkus sebagai parameter dan menggunakannya kembali dalam situasi lain seperti ini.

rsp
sumber
3

Saya juga harus memfilter berdasarkan pola URL (/ {servicename} / api / stats /) dalam kode java.

if (path.startsWith("/{servicename}/api/statistics/")) {
validatingAuthToken(((HttpServletRequest) request).getHeader("auth_token"));
filterChain.doFilter(request, response);            
}

Tetapi anehnya, servlet itu tidak mendukung pola url selain (/ *), Ini seharusnya menjadi kasus yang sangat umum untuk API servlet!

Sindhu
sumber
0

Saya telah menemukan masalah yang sama, tetapi saya menemukan server lain di bawah ini.

web.xml

 <!-- set this param value for the filter-->
    <init-param>
            <param-name>freePages</param-name>
            <param-value>
            MainFrame.jsp;
            </param-value>
    </init-param>

filter.java

strFreePages = config.getInitParameter("freePages"); //get the exclue pattern from config file
isFreePage(strRequestPage)  //decide the exclude path

dengan cara ini Anda tidak perlu melecehkan kelas Filter beton.

nelson
sumber
0

Jika karena alasan apa pun Anda tidak dapat mengubah pemetaan filter asli ("/ *" dalam kasus saya) dan Anda mengirim ke filter pihak ketiga yang tidak dapat diubah, Anda dapat menemukan hal-hal berikut yang bermanfaat:

  • Mencegah jalan yang harus dilewati
  • Lewati dan jalankan cincin terakhir rantai filter (servlet itu sendiri)
  • Melompati dilakukan melalui refleksi, memeriksa contoh wadah dalam mode debug

Karya-karya berikut di Weblogic 12.1.3:

      import org.apache.commons.lang3.reflect.FieldUtils;
      import javax.servlet.Filter;

      [...]

      @Override   
      public void doFilter(ServletRequest request, ServletRespons response, FilterChain chain) throws IOException, ServletException { 
          String path = ((HttpServletRequest) request).getRequestURI();

          if(!bypassSWA(path)){
              swpFilterHandler.doFilter(request, response, chain);

          } else {
              try {
                  ((Filter) (FieldUtils.readField(
                                (FieldUtils.readField(
                                        (FieldUtils.readField(chain, "filters", true)), "last", true)), "item", true)))
                  .doFilter(request, response, chain);
              } catch (IllegalAccessException e) {
                  e.printStackTrace();
              }           
          }   
      }
Robertmann
sumber