Рубрики
Без рубрики

Фильтры запросов на загрузку JAVA Spring Boot

Intro Filter предоставляет удобный механизм для проверки и фильтрации HTTP-запросов en… Помеченный java, spring boot, spring, ratelimit.

Filter предоставляет удобный механизм для проверки и фильтрации HTTP-запросов, поступающих в ваше приложение. Например, вы хотите ввести пользовательский заголовок в запрос/ответ на основе некоторых условий, или вы хотите выполнить некоторые проверки перед доступом к контроллеру и обработкой запроса.

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

Используйте Filters , которые он запускает перед доступом к контроллерам и обслуживанием ваших запросов.

Как писать?

Для того, чтобы создать фильтр запросов. вам просто нужно создать класс, который реализует интерфейс Filter .

package com.ahmedash95.example.filters;

import javax.servlet.*;

@Component
public class RoomsCreateFilter implements Filter 
{
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
     // your code goes here.

    // then you need to pass it in the chain. 
    chain.doFilter(request, response);
  }
}

Если вы не знакомы с приведенным выше кодом. вот некоторые пояснения:

  • @Component – это аннотация, которая позволяет нам зарегистрировать класс как компонент в ApplicationContext
  • реализует фильтр интерфейс сообщает Spring, как следует использовать этот класс при регистрации через |/@Component импортируйте javax.servlet. *;
  • импортирует Filter, ServletRequest, ServletResponse, FilterChain и ServletException. цепь.doFilter(запрос, ответ);
  • в конце обязательно. поскольку он сообщает spring, как продолжить обработку запроса. без этого ответ будет пустым, так как цепочка была разорвана.

Возвращать ответ на ошибки

Если вы что-то проверяете и хотите прекратить обработку запроса и просто вернуть ответ. вы можете просто изменить объект Response и вернуть его из вашего класса Filter.

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    boolean valid = false; // check for something
    if (!valid) {
        ((HttpServletResponse) response).setStatus(422);
        response.getOutputStream().write("Validation error".getBytes());
        return;
    }


    chain.doFilter(request, response);
}

Примеры

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

1.Убедитесь, что ключ API предоставлен и действителен

Допустим, вы работаете над API, который что-то предоставляет. и вы хотите убедиться, что все запросы к вашему приложению будут иметь ключ API, а ключ действителен. конечно, вы не хотите выполнять проверку ключа в каждом контроллере и методе.

Давайте начнем с создания вашего фильтра в первую очередь

package com.example.demo;

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;

@Component
public class APIKeyValidatorFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        String key = req.getHeader("X-API-KEY");
        if(key == null) {
            ((HttpServletResponse) response).setStatus(401);
            response.getOutputStream().write("API Key is missing!".getBytes());
            return;
        }

        if(!KeyValidator.valid(key)) {
            ((HttpServletResponse) response).setStatus(403);
            response.getOutputStream().write("API Key is invalid".getBytes());
            return;
        }

        chain.doFilter(request, response);
    }
}

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

2.Ограничение скорости для некоторых конечных точек

Допустим, вы хотите защитить POST/comment конечная точка. таким образом, ваши пользователи не должны иметь возможности отправлять более 2 комментариев за одну минуту. Опять же это можно сделать в контроллере, но это не лучшее место для этого.

Давайте создадим класс Filter

package com.example.demo;

import javax.servlet.Filter;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;

public class PostCommentRateLimit implements Filter
{
    RateLimiter rateLimiter;

    public PostCommentRateLimit(RateLimiter rateLimiter) {
        this.rateLimiter = rateLimiter;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        int userId = 1; // Get User
        boolean valid = rateLimiter.validate(String.format("ratelimit.user.comments:%d", userId), 2);
        if(!valid) {
            ((HttpServletResponse) response).setStatus(429);
            response.getOutputStream().write("Too Many Requests".getBytes());
            return;
        }


        chain.doFilter(request, response);
    }
}

Здесь нужно кое-что объяснить:

  • Мы не использовали @Component здесь. потому что мы хотим применить фильтр к СООБЩЕНИЮ/комментарию только конечная точка. так что дальше мы зарегистрируем его сами.
  • У нас есть Ограничитель скорости в конструкторе. и вам не нужно беспокоиться об этом. используйте любую библиотеку, соответствующую вашим потребностям.

Как уже упоминалось мы не использовали @Component потому что он применит фильтр для всех запросов. вместо этого мы создадим другой класс для регистрации наших фильтров так, как мы хотим.

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RequestsFilterRegister {

    @Autowired
    RateLimiter rateLimiter;

    @Bean
    public FilterRegistrationBean registerPostCommentsRateLimiter(){
        FilterRegistrationBean registrationBean  = new FilterRegistrationBean<>();

        registrationBean.setFilter(new PostCommentRateLimit(rateLimiter));
        registrationBean.addUrlPatterns("/comment");

        return registrationBean;
    }
}
  • Мы создали Запросы регистрируются фильтром как @Configuration class
  • Единственный метод там зарегистрируйте ограничитель скорости комментариев , чтобы зарегистрировать фильтр так, как мы хотим.
  • мы использовали add Url Patterns , чтобы применить фильтр только к /comment конечной точке.

Теперь у нас есть одна маленькая проблема. фильтр применяется к любому методу в /комментарии либо ПОЛУЧИТЬ, либо ОПУБЛИКОВАТЬ. и чтобы исправить это, нам просто нужно изменить PostCommentRateLimit@doFilter , чтобы пропустить, если метод не post

HttpServletRequest req = (HttpServletRequest) request;
if (!req.getMethod().equals("POST")) {
    chain.doFilter(request, response);
    return;
}

Сейчас полный класс – это

package com.example.demo;

import javax.servlet.Filter;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class PostCommentRateLimit implements Filter
{
    RateLimiter rateLimiter;

    public PostCommentRateLimit(RateLimiter rateLimiter) {
        this.rateLimiter = rateLimiter;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        if (!req.getMethod().equals("POST")) {
            chain.doFilter(request, response);
            return;
        }

        int userId = 1; // Get User
        boolean valid = rateLimiter.validate(String.format("ratelimit.user.comments:%d", userId), 2);
        if(!valid) {
            ((HttpServletResponse) response).setStatus(429);
            response.getOutputStream().write("Too Many Requests".getBytes());
            return;
        }


        chain.doFilter(request, response);
    }
}

Теперь вы знаете, что такое Фильтры запросов , как их писать, и вы применили несколько реальных примеров. также вы видели, как настроить маршруты и методы для применения фильтров.

Полный исходный код https://github.com/ahmedash95/java-spring-request-filters

Для получения дополнительной информации вы можете проверить baeldung.com/spring-boot-add-filter

Примечание

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

Оригинал: “https://dev.to/ahmedash95/java-springboot-request-filters-15ha”