SpringBoot请求过滤器的应用案例


Filter提供了一种检查和过滤进入您的应用程序的HTTP请求的便捷机制。例如,您要基于某些条件将自定义标头注入到请求/响应中,或者要在访问控制器并处理请求之前运行一些检查。

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中将类注册为Bean
  • implements Filter 接口告诉Spring通过进行注册时应如何使用此类 @Component
  • import javax.servlet.*; 导入Filter,ServletRequest,ServletResponse,FilterChain和ServletException。
  • chain.doFilter(request, response);最后是必须的。因为它告诉spring如何继续处理请求。没有它,由于链条断裂,响应将是空的。

如果您验证了某些内容并且想要停止处理请求,而只返回响应。您只需修改Response对象并从Filter类中将其返回即可。
一些真实的例子和一些不同的场景,以确保您了解整个情况。
1.确保提供了API密钥并且有效
假设您正在使用提供某些内容的API。并且您要确保对您的应用程序的所有请求都具有API密钥并且该密钥有效。当然,您不想在每个控制器和方法中都进行密钥验证。
首先创建过滤器:

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.Ratelimit一些端点
假设您要保护POST /comment端点。因此您的用户一分钟内提交的评论不得超过2条。同样,它可以在控制器中完成,但这并不是最佳选择。
让我们创建Filter类:

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。因为我们只想将过滤器应用于POST /comment端点。因此我们接下来将自己注册。
  • 我们有RateLimiter构造函数。而且您不必担心。使用适合您需求的任何库。

我们将创建另一个类以所需的方式注册过滤器:

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<PostCommentRateLimit> registerPostCommentsRateLimiter(){
        FilterRegistrationBean<PostCommentRateLimit> registrationBean  = new FilterRegistrationBean<>();

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

        return registrationBean;
    }
}

  • 我们创建RequestsFilterRegister作为@Configuration类
  • registerPostCommentsRateLimiter唯一需要方法是注册过滤器。
  • 使用addUrlPatterns应用过滤器作用于/comment端点。

现在我们有一个小问题。过滤器将应用于/commentGET或POST上以外的任何方法。并解决此问题,
PostCommentRateLimit@doFilter如果方法未发布,我们只需修改即可跳过:
HttpServletRequest req = (HttpServletRequest) request;
if (!req.getMethod().equals("POST")) {
    chain.doFilter(request, response);
    return;
}