如何在Java Spring控制器中转义请求体中的引号?
当然可以用Spring实现请求参数的引号转义!
不过先敲个重点:直接转义引号来做SQL净化并不是最安全的方案,优先推荐你用预编译SQL语句(比如JDBC的PreparedStatement)或者ORM框架(JPA/Hibernate)来处理SQL查询,这才是防止SQL注入的标准做法。但如果你确实需要对请求中的引号做转义处理,下面几种Spring的实现方式可以帮到你:
方法1:在控制器中用@InitBinder处理参数
@InitBinder是Spring提供的用于初始化WebDataBinder的注解,你可以利用它在参数绑定到控制器方法前,对请求参数做自定义处理。针对你用@RequestBody接收Map的场景,可以这样写:
@RestController public class YourController { @InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Map.class, new PropertyEditorSupport() { @Override public void setValue(Object value) { if (value instanceof Map) { Map<String, String> paramMap = (Map<String, String>) value; // 遍历Map,对每个value的引号进行转义 paramMap.replaceAll((key, val) -> { if (val == null) return null; // 这里以SQL常用的单引号转义为例:把'换成'' return val.replace("'", "''"); // 如果是需要转义双引号,可以换成val.replace("\"", "\\\""); }); super.setValue(paramMap); } else { super.setValue(value); } } }); } @RequestMapping(method = RequestMethod.POST) public List<String[]> myEndpoint(@RequestBody Map<String, String> params, @AuthenticationPrincipal Account connectedUser) throws Exception { // 这里的params已经完成了引号转义 return myService.runQuery(params, connectedUser); } }
这个方法只作用于当前控制器,如果只有这一个端点需要处理,用这个很方便。
方法2:全局自定义RequestBodyAdvice处理
如果你的多个控制器都需要做引号转义,全局处理会更高效。你可以实现RequestBodyAdvice接口,在请求体被解析后统一处理:
@ControllerAdvice public class QuoteEscapeRequestBodyAdvice implements RequestBodyAdvice { @Override public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { // 只处理目标类型为Map<String, String>的请求体 return targetType instanceof ParameterizedType && ((ParameterizedType) targetType).getRawType().equals(Map.class) && ((ParameterizedType) targetType).getActualTypeArguments()[0].equals(String.class) && ((ParameterizedType) targetType).getActualTypeArguments()[1].equals(String.class); } @Override public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException { return inputMessage; } @Override public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { if (body instanceof Map) { Map<String, String> paramMap = (Map<String, String>) body; paramMap.replaceAll((key, val) -> { if (val == null) return null; // 同样按需求转义引号,这里示例转义单引号 return val.replace("'", "''"); }); return paramMap; } return body; } @Override public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { return body; } }
加上@ControllerAdvice注解后,这个处理逻辑会对所有符合条件的@RequestBody请求生效,不用在每个控制器重复写代码。
重要提醒
- 转义规则要根据你的SQL方言调整:比如MySQL用
\'转义单引号,而Oracle用''转义,一定要对应正确。 - 再次强调:转义引号不能完全防止SQL注入,比如如果参数里包含特殊字符组合,还是可能存在风险。强烈建议你改用预编译语句或者ORM框架来构建SQL,从根源上避免注入问题。
内容的提问来源于stack exchange,提问作者Matthias Beaupère
相关产品推荐
相关产品推荐

