springboot/spring/springmvc 去除前台传来的string字符串自动去除前后面的空格

该文章展示了如何在SpringMVC中配置自定义的HttpMessageConverter,特别是使用Jackson库处理JSON反序列化时忽略未知属性,并添加了一个去除字符串空格的Deserializer。同时,文章还提供了防止XSS攻击的策略,通过创建自定义的StringEscapeEditor进行输入转义。

 

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.xxx.convertor.StringWithoutSpaceDeserializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class HttpMessageConvertor implements WebMvcConfigurer {

    @Autowired
    private ObjectMapper mapper;

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(mappingJackson2HttpMessageConverter());
    }

    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        SimpleModule module = new SimpleModule();
        module.addDeserializer(String.class, new StringWithoutSpaceDeserializer(String.class));
        mapper.registerModule(module);

        converter.setObjectMapper(mapper);

        return converter;
    }
}

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;

public class StringWithoutSpaceDeserializer extends StdDeserializer<String> {

    private static final long serialVersionUID = -6972065572263950443L;

    public StringWithoutSpaceDeserializer(Class<String> vc) {
        super(vc);
    }

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        return p.getText() != null ? p.getText().trim() : null;
    }
}

import com.xxx.util.security.StringEscapeEditor;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 自定义Web绑定初始化器
 * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport 的getConfigurableWebBindingInitializer方法
 */
@Configuration
@ControllerAdvice
public class WebBindingInitializerConfiguration {
//    final ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
//    final FormattingConversionService conversionService = new DefaultFormattingConversionService();

//    @Bean
//    public ConfigurableWebBindingInitializer configurableWebBindingInitializer(FormattingConversionService conversionService, Validator mvcValidator) {
//        ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
//        initializer.setConversionService(conversionService);
//        initializer.setValidator(mvcValidator);
//        //装配自定义属性编辑器
//        initializer.setPropertyEditorRegistrar(propertyEditorRegistry -> {
//            //PropertyEditors并不是线程安全的,对于每一个请求,我们都需要new一个PropertyEditor对象
//            propertyEditorRegistry.registerCustomEditor(String.class, new StringEscapeEditor());
//            propertyEditorRegistry.registerCustomEditor(Date.class, new DateEditor());
//        });
//        return initializer;
//    }
    @Bean
    public ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
        ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
        FormattingConversionService conversionService = new DefaultFormattingConversionService();
        //we can add our custom converters and formatters
        //conversionService.addConverter(...);
        //conversionService.addFormatter(...);
        initializer.setConversionService(conversionService);
        //we can set our custom validator
        //initializer.setValidator(....);

        //here we are setting a custom PropertyEditor
        initializer.setPropertyEditorRegistrar(propertyEditorRegistry -> {
            SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
            propertyEditorRegistry.registerCustomEditor(Date.class,
                    new CustomDateEditor(dateFormatter, true));

            propertyEditorRegistry.registerCustomEditor(String.class,
                    new StringEscapeEditor());
        });
        return initializer;
    }
}

import org.springframework.web.util.HtmlUtils;
import org.springframework.web.util.JavaScriptUtils;

import java.beans.PropertyEditorSupport;
import java.util.Objects;

/**
 * 
 * @description 与spring mvc的@InitBinder结合 用于防止XSS攻击
 */
public class StringEscapeEditor extends PropertyEditorSupport {

    /** 转义HTML */
    private boolean escapeHTML;

    /** 转义javascript */
    private boolean escapeJavaScript;

    /** 是否将空字符串转换为null */
    private final boolean emptyAsNull;

    /** 是否去掉前后空格 */
    private final boolean trimmed;

    public StringEscapeEditor() {
        this(true,true,false,false);
    }

    public StringEscapeEditor(boolean escapeHTML, boolean escapeJavaScript) {
        this(true,true,escapeHTML,escapeJavaScript);
    }

    public StringEscapeEditor(boolean emptyAsNull,boolean trimmed, boolean escapeHTML, boolean escapeJavaScript) {
        super();
        this.emptyAsNull = emptyAsNull;
        this.trimmed = trimmed;
        this.escapeHTML = escapeHTML;
        this.escapeJavaScript = escapeJavaScript;
    }

    @Override
    public String getAsText() {
        Object value = getValue();

        if(Objects.nonNull(value))
        {
            return value.toString();
        }
        return value != null ? value.toString() : null;
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {

        String value = text;

        if (value == null || emptyAsNull && text.isEmpty()) {
            //do nothing
        } else if (trimmed) {
            value = value.trim();
        }

        if (escapeHTML) {
            //HTML转义(防止XSS攻击)
            //HtmlUtils.htmlEscape 默认的是ISO-8859-1编码格式,会将中文的某些符号进行转义。
            //如果不想让中文符号进行转义请使用UTF-8的编码格式。例如:HtmlUtils.htmlEscape(text, "UTF-8")
            value = HtmlUtils.htmlEscape(value);
        }
        if (escapeJavaScript) {
            //HTML转义(防止XSS攻击)
            //HtmlUtils.htmlEscape 默认的是ISO-8859-1编码格式,会将中文的某些符号进行转义。
            //如果不想让中文符号进行转义请使用UTF-8的编码格式。例如:HtmlUtils.htmlEscape(text.trim(), "UTF-8")
            value = JavaScriptUtils.javaScriptEscape(value);
        }
        setValue(value);
    }

}

Spring MVC 中,当请求入参为自定义 Java 对象但实际传入字符串字符串含大量空格导致类转换异常时,可以使用 Mvc 拦截器处理这些空格。不过,拦截器并非专门用于处理参数内容,更适合在请求处理、后做一些通用的逻辑判断、日志记录等操作,直接处理参数内容不是它的主要职责。 可以通过以下几种方式结合拦截器或使用其他更合适的手段来处理空格: - **使用拦截器结合反射**:在拦截器的 `preHandle` 方法中,获取请求的参数,使用反射遍历自定义 Java 对象的属性,如果是字符串类型,对其进行 `trim` 操作。不过这种方式存在性能问题,若对象嵌套层次深,需要递归处理,性能代价大[^1]。 ```java import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Field; import org.springframework.web.servlet.HandlerInterceptor; public class TrimInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object target = getTargetFromRequest(request); if (target != null) { trimStringFields(target); } return true; } private Object getTargetFromRequest(HttpServletRequest request) { // 这里需要根据实际情况从 request 中获取自定义 Java 对象 return null; } private void trimStringFields(Object obj) throws IllegalAccessException { Class<?> clazz = obj.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); Object value = field.get(obj); if (value instanceof String) { field.set(obj, ((String) value).trim()); } else if (value != null) { trimStringFields(value); } } } } ``` - **重写 `DataBinder`**:`DataBinder` 是 Spring MVC 中进行入参绑定的类,可以重写它的相关方法,在参数绑定过程中对字符串进行 `trim` 操作。不过这种方式改动较大,可能会影响到所有参数的处理,有一定风险[^1]。 - **使用 `StringTrimmerEditor` + 重写 `ObjectMapper`**:Spring MVC 提供了很多扩展点,可以通过注册的方式来处理字符串空格问题,这种方式相对更合适,避免了两种方式的一些缺点[^1]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值