springboot 项目通过过滤器(Filter)实现白名单过滤以及拦截客户端请求服务接口进行统一日志记录的实现方式

2023-04-01 11:24:59 浏览数 (1)

创建一个自定义的过滤器

代码语言:javascript复制
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@WebFilter(urlPatterns = "/*")
public class HttpReqFilter implements Filter {

	@Override
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
			throws IOException, ServletException {
		 HttpServletRequest request = (HttpServletRequest) req;
	        HttpServletResponse response = (HttpServletResponse) res;
	        // 头攻击检测  过滤主机名(非白名单中的直接返回 403)
	        String serverName = request.getServerName();
	        if (serverName != null && !checkBlankList(serverName)) {
	        	System.out.println("[serverName deny access tips]->"   serverName);
	            response.setStatus(403);
	            return;
	        }
	        // 转换成代理类(此处是为了获取返回参数)
	        ResponseWrapper wrapperResponse = new ResponseWrapper(response);
	        filterChain.doFilter(request, wrapperResponse);
	        // @todo 记录接口调用日志,超长参数和结果忽略
	        String resData = "";
	        // 返回值字节
	        byte[] content = wrapperResponse.getContent();
	        // 判断是否有值
	        if (content.length > 0) {
	        	resData = new String(content, "UTF-8");
	        	// 调用链
	            if (!isJson(resData)) {
	                // 如果不是json数据,可能是返回的数据流或下载文件,直接返回原数据
	                //把返回值输出到客户端
	                ServletOutputStream out = response.getOutputStream();
	                out.write(content);
	                out.flush();
	            } else {
	                // 处理返回数据
	                String cipherText = null;
	                try {
	                    cipherText = resData;
	                    //......根据需要处理返回值
	                } catch (Exception e) {
	                    e.printStackTrace();
	                }
	                //把返回值输出到客户端
	                ServletOutputStream out = response.getOutputStream();
	                out.write(cipherText.getBytes());
	                out.flush();
	            }
	        }
	        if (resData.length() > 1024) {
	        	resData = "...";
			}
	        JSONObject reqParam = new JSONObject();
	        Map<String, String[]> parameterMap = request.getParameterMap();
	        for (Entry<String, String[]> entry : parameterMap.entrySet()) {
				String key = entry.getKey();
				String[] vals = entry.getValue();
				if(vals.length>0) {
					reqParam.put(key, vals[0]);
				}else {
					reqParam.put(key, "");
				}
			}
	        String params = reqParam.toJSONString();
	        if (params.length() > 1024) {
				params = "...";
			}
			//输出日志
			log.info(
					"#{}#{}#{}#{}",
					request.getRemoteAddr(),
					request.getRequestURI(),
					params,
					resData
			);
	}
	
	//判断主机是否存在白名单中
    private boolean checkBlankList(String serverName){
        if(serverName.equals("127.0.0.1"||serverName.equals("localhost")){//此处为自己网站的主机地址
            return true;
        }
        return false;
    }
    
    /**
     * 判断字符串数据是否为json
     * @param content
     * @return
     */
    public static boolean isJson(String content) {
        try {
            JSON.parseObject(content);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

启动类添加注解

@ServletComponentScan

到此就配置完成了,是不是非常简单呢?

查看拦截host是否生效

这里就会看到我们拦截的 host 请求。

查看日志输出

具体输出什么内容,这里可以通过自己的业务需要来做不同的输出。我这里记录的是ip,请求参数,响应内容。

0 人点赞