You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java环境下如何在请求到达控制器前拦截校验特殊字符(Hybris场景)

Hybris Storefront入站请求特殊字符校验解决方案

之前方案失效原因

HandlerInterceptor的执行时机晚于Servlet过滤器链,若仅在拦截器中包装请求,此前Hybris内置过滤器可能已经读取过原始请求体,且未将包装后的请求替换到请求链中,后续组件拿到的仍是原始已被消费的请求对象,因此会出现请求体为空的问题。


方案1:过滤器+自定义可重复读请求包装器(推荐全局校验场景)

该方案从Servlet层替换请求对象,彻底解决请求体不可重复读的问题,适配所有类型的POST请求校验。

步骤1:实现可重复读请求包装类

public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {
    private final byte[] cachedBody;

    public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
        super(request);
        InputStream requestInputStream = request.getInputStream();
        this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return byteArrayInputStream.available() == 0;
            }
            @Override
            public boolean isReady() {
                return true;
            }
            @Override
            public void setReadListener(ReadListener readListener) {}
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(cachedBody);
        return new BufferedReader(new InputStreamReader(byteArrayInputStream, getCharacterEncoding()));
    }

    public byte[] getCachedBody() {
        return this.cachedBody;
    }
}

步骤2:实现全局校验过滤器

@Component
public class SpecialCharacterValidateFilter extends OncePerRequestFilter {
    // 特殊字符正则,可按需调整规则
    private static final Pattern SPECIAL_CHAR_PATTERN = Pattern.compile("[<>/\\\\|&!@#$%^*()+\\[\\]{}:;\"',?/~`]");

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String contentType = request.getContentType();
        // 跳过文件上传、非JSON请求,表单请求可直接通过request.getParameterMap()校验无需读流
        if (contentType == null || !contentType.contains(MediaType.APPLICATION_JSON_VALUE) || contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
            filterChain.doFilter(request, response);
            return;
        }

        // 提前包装请求缓存请求体
        CachedBodyHttpServletRequest wrappedRequest = new CachedBodyHttpServletRequest(request);
        // 校验请求体
        String body = new String(wrappedRequest.getCachedBody(), StandardCharsets.UTF_8);
        if (SPECIAL_CHAR_PATTERN.matcher(body).find()) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.getWriter().write("{\"code\":\"400\",\"message\":\"请求包含非法特殊字符\"}");
            return;
        }
        // 校验通过,将包装后的请求传入后续链路
        filterChain.doFilter(wrappedRequest, response);
    }
}

步骤3:注册过滤器到Hybris链路

在Hybris的web.xml或者Spring配置中注册该过滤器,优先级设置到RequestContextFilter之前,确保请求在被其他组件读取前完成包装。


方案2:Spring AOP控制器层校验(推荐轻量场景)

该方案完全避免操作请求流,适配Hybris复杂过滤器链的兼容性更好,无需调整原有链路配置。

实现代码示例

@Aspect
@Component
public class SpecialCharacterValidateAspect {
    private static final Pattern SPECIAL_CHAR_PATTERN = Pattern.compile("[<>/\\\\|&!@#$%^*()+\\[\\]{}:;\"',?/~`]");
    private final ObjectMapper objectMapper = new ObjectMapper();

    // 切点替换为你项目控制器的实际包路径
    @Pointcut("execution(* com.your.storefront.controllers..*(..))")
    public void controllerPointcut() {}

    @Around("controllerPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        Object[] args = joinPoint.getArgs();

        // 遍历所有RequestBody标注的参数做校验
        for (int i = 0; i < parameterAnnotations.length; i++) {
            for (Annotation annotation : parameterAnnotations[i]) {
                if (annotation instanceof RequestBody) {
                    String json = objectMapper.writeValueAsString(args[i]);
                    if (SPECIAL_CHAR_PATTERN.matcher(json).find()) {
                        throw new IllegalArgumentException("请求包含非法特殊字符");
                    }
                }
            }
        }
        return joinPoint.proceed();
    }
}

校验不通过的异常可通过全局异常处理器统一封装返回格式。


注意事项

  • 表单提交(application/x-www-form-urlencoded)请求无需读请求流,直接调用request.getParameterMap()获取参数校验即可,不会触发请求体清空
  • 特殊字符规则建议统一维护在项目配置文件中,方便后续迭代调整
  • 校验逻辑需跳过文件上传类请求,避免读取二进制内容出现乱码误判

内容的提问来源于stack exchange,提问作者Himanshu Jain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 16:24:04