通用拦截器,进行请求日志的打印

597 阅读3分钟

Sping拦截器

它可以根据 URL 对请求进行拦截,主要应用于登陆校验、权限验证、乱码解决、性能监控和异常处理等功能上

HandlerInterceptorAdapter 和 HandlerInterceptor

自定义拦截器,首先继承HandlerInterceptorAdapter或者实现HandlerInterceptor,没有什么区别,因为Java 8开始defalut 修饰的方法,默认继承,不需要实现。建议采用实现HandlerInterceptor,面向接口编程,而且Java只能单继承,但接口可以多实现。

方法声明描述
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)该方法在控制器处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)该方法在控制器处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步修改。
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)该方法在视图渲染结束后执行,可以通过此方法实现资源清理、记录日志信息等工作。

代码实现

CommonHttpInterceptor

@Slf4j
public class CommonHttpInterceptor implements HandlerInterceptor {

    private static final String UNKNOWN = "unknown";
    private static final String MONITOR_HEALTH = "/monitor/health";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        long start = SystemClockUtil.millisClock().now();
        String url = request.getRequestURL().toString();
        // 接口过滤打印
        if (url.contains(MONITOR_HEALTH)) {
            return true;
        }

        String method = request.getMethod();
        String queryString = "";
        // 去掉最后一个空格
        Map<String, String[]> params = request.getParameterMap();
        for (String key : params.keySet()) {
            String[] values = params.get(key);
            for (String value : values) {
                queryString += key + "=" + value + "&";
            }
        }
        // URL 参数
        queryString = "".equals(queryString) ? null : queryString.substring(0, queryString.length() - 1);

        // body 参数
        RequestWrapper requestWrapper = new RequestWrapper(request);
        String bodyParams = HttpParamsUtil.replace(requestWrapper.getBody());

        // header参数
        String headersParams = HttpParamsUtil.getHeadersInfo(requestWrapper);

        // 公网IP
        String cIp = CommonHttpInterceptor.getIpAddress(request);

        long end = System.currentTimeMillis();

        log.info(String.format(
                "应用请求参数  url: %s, method: %s, query-params: %s, body-params: %s, headers-params: %s, c-ip: %s, run-time: %s",
                url, method, queryString, bodyParams, headersParams, cIp, (end - start) + ""));

        return true;
    }

    /**
     * 获取用户真实IP地址
     *
     * @param request 请求参数
     * @return ip地址
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        //不管转发多少次,取第一位
        String remoteIp = ip.split(",")[0];
        MDC.put("remote_ip", remoteIp);
        return remoteIp;
    }
}

注册拦截器

CommonHttpInterceptor交给Spring管理,自定义WebCommonConfiguration实现WebMvcConfigurer接口,下篇写WebMvcConfigurer

@Configuration
public class WebCommonConfiguration implements WebMvcConfigurer {

    /**
     * 初始化通用拦截器
     *
     * @return bean
     */
    @Bean
    public HandlerInterceptor getInterceptor() {
        return new CommonHttpInterceptor();
    }

    /**
     * 可定义多个拦截器,先添加的先执行
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 定义过滤拦截的url名称,拦截所有请求
        registry.addInterceptor(this.getInterceptor()).addPathPatterns("/**");
        // 定义登录权限校验拦截器,默认也是拦截全部请求,与登录相关的就不用拦截了,自定义就好
        registry.addInterceptor(this.authInterceptor()).addPathPatterns("/**")
        .excludePathPatterns("/user/login");
    }
 }

登录拦截器

伪代码,仅供参考,结合自身业务实现

@Slf4j
public class AuthInterceptor implements HandlerInterceptor {
  
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try {
            String token = HttpParamsUtil.getRequestToken(request);
            if (StringUtils.isBlank(token)) {
                log.info("请求缺少token凭证,拒绝访问");
                responseMessage(response, CommonResponse.error(ServerCode.UNAUTHORIZED));
                return false;
            }
            // 检验token是否合法
 
            // 检查是否登录
            if (SysUserLoginStatusEnum.NOT_LOGIN.getStatus().equals(dto.getUserLoginStatus())) {
                log.info("授权已过期,请重新登录 token:{}", token);
                responseMessage(response, CommonResponse.error(ServerCode.UNAUTHORIZED, "授权已过期,请重新登录"));
                return false;
            }
            if (!SysUserLoginStatusEnum.CURRENT_LOGIN.getStatus().equals(dto.getUserLoginStatus())) {
                responseMessage(response, CommonResponse.error(ServerCode.UNAUTHORIZED));
                return false;
            }
            // 获取当前用户信息
            // 设置当前用户信息到本地线程
            // UserContext.setLoginUser(loginUser);
        } catch (Exception e) {
            log.info("校验token失败:{}",e.getMessage());
            responseMessage(response, CommonResponse.error(ServerCode.UNAUTHORIZED));
            return false;
        }
        return true;
    }
    
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
         // 清除上下文信息,一般都是用户信息
        // UserContext.clearUser();
    }

    /**
     * 将错误信息转换为json 放到 HttpServletResponse
     */
    public static void responseMessage(HttpServletResponse response, CommonResponse errorResponse) throws IOException {
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out = response.getWriter();
        out.print(JSONObject.toJSONString(errorResponse));
        out.flush();
        out.close();
    }
    }{}

参考连接: c.biancheng.net/spring_boot…