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

批量赋值安全问题:如何在Jersey框架中使用Spring的@InitBinder?

解决Jersey应用中的Mass Assignment安全问题:属性过滤方案

首先得给你提个醒:你在代码里用的Spring MVC的@InitBinder注解,在Jersey框架里是不会生效的——这俩框架的请求数据绑定机制完全独立,Jersey根本不识别这个Spring专属的注解。不过别担心,我们有好几种方法可以实现你想要的「只允许设置指定属性,其余设为null」的需求,下面逐个给你讲:


方法1:用Jackson注解实现字段白名单(最简单)

因为Jersey默认用Jackson处理JSON请求/响应,所以直接在你的AccountRequest实体类上添加Jackson注解就能轻松实现过滤:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

// 严格模式:只允许下面标注@JsonProperty的字段被赋值,其他字段直接忽略
@JsonIgnoreProperties(value = {"*"}, allowSetters = false)
public class AccountRequest {

    // 只允许这个字段被设置
    @JsonProperty("allowedField")
    private String allowedField;

    // 其他字段不标注@JsonProperty,反序列化时不会被赋值(保持null)
    private String disallowedField1;
    private Integer disallowedField2;

    // getter和setter方法...
}

如果只是想忽略未知字段(比如请求里多传的字段不处理),也可以用更宽松的配置:

@JsonIgnoreProperties(ignoreUnknown = true)
public class AccountRequest {
    // ...
}

这种方式下,请求里存在但实体类没有的字段会被跳过,实体类已有的字段还是会被正常赋值——如果你需要严格的白名单,还是用第一种注解配置更安全。


方法2:自定义Jersey MessageBodyReader(灵活可控)

如果需要针对不同接口设置不同的字段白名单(比如同一个实体类在不同接口里允许的字段不一样),可以自定义Jersey的MessageBodyReader,手动控制请求体的解析过程:

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.ext.MessageBodyReader;
import jakarta.ws.rs.ext.Provider;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;

@Provider
@Consumes(MediaType.APPLICATION_JSON)
public class AccountRequestReader implements MessageBodyReader<AccountRequest> {

    private final ObjectMapper objectMapper = new ObjectMapper();

    // 指定这个Reader只处理AccountRequest类型的参数
    @Override
    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return type == AccountRequest.class;
    }

    @Override
    public AccountRequest readFrom(Class<AccountRequest> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
        JsonNode requestNode = objectMapper.readTree(entityStream);
        AccountRequest filteredRequest = new AccountRequest();

        // 只设置你允许的字段,其他字段保持null
        if (requestNode.has("allowedField")) {
            filteredRequest.setAllowedField(requestNode.get("allowedField").asText());
        }
        // 可以根据接口的注解(比如自定义注解)动态调整允许的字段,这里简化处理
        return filteredRequest;
    }
}

写完这个类后,记得把它注册到Jersey的配置类里(比如继承ResourceConfig的类):

public class JerseyConfig extends ResourceConfig {
    public JerseyConfig() {
        register(AccountRequestReader.class);
        register(RequestService.class);
        // 其他注册项...
    }
}

方法3:Spring集成场景下用RequestBodyAdvice

如果你的项目是Spring Boot集成Jersey的(比如用spring-boot-starter-jersey依赖),可以借助Spring的RequestBodyAdvice来实现全局的请求体过滤:

import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;

import java.lang.reflect.Type;

@ControllerAdvice
public class RequestPropertyFilterAdvice extends RequestBodyAdviceAdapter {

    // 指定只处理AccountRequest类型的请求体
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return targetType.getTypeName().equals(AccountRequest.class.getName());
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        AccountRequest originalRequest = (AccountRequest) body;
        // 创建新实例,只复制允许的字段,确保其他字段为null
        AccountRequest filteredRequest = new AccountRequest();
        filteredRequest.setAllowedField(originalRequest.getAllowedField());
        // 其他不允许的字段不复制,保持默认null
        return filteredRequest;
    }
}

这种方式适合需要全局统一过滤的场景,Spring会自动拦截所有符合条件的请求体,帮你完成过滤。


最后给你修正一下原来的代码里的问题:把无效的@InitBinder方法去掉,换成上面的任意一种方案就行。比如用Jackson注解的话,你的RequestService可以保持不变,只需要修改AccountRequest类。

内容的提问来源于stack exchange,提问作者Md. Ruzdi Islam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 03:28:13