SpringBoot @JsonIgnore 忽略字段,但也不会接收使用@JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 解决只接收不返回。
Spring Boot 中字段序列化与反序列化的精准控制:从 @JsonIgnore 到 @JsonProperty
在 Spring Boot 开发中,处理 JSON 数据时经常遇到一个痛点:如何精确控制字段的“输入”与“输出”。很多时候,我们希望某个字段在返回给前端时被隐藏(如密码、内部ID),但在接收前端提交的数据时又需要能够被赋值。简单地使用 @JsonIgnore 往往会导致“一刀切”,既忽略了返回,也阻断了接收。本文将深入探讨这一问题的根源,并展示如何使用 @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 实现更精细的控制。
为什么 @JsonIgnore 不够用?
Jackson 是 Spring Boot 默认的 JSON 处理库。@JsonIgnore 注解的作用非常直观:它告诉 Jackson 在序列化(对象转 JSON)和反序列化(JSON 转对象)过程中都忽略该字段。
public class User {
private String username;
@JsonIgnore
private String password;
}
在上述代码中,当我们将 User 对象转换为 JSON 返回给客户端时,password 字段确实不会出现在 JSON 字符串中。然而,这也带来了一个副作用:如果客户端在注册或修改密码时提交了 password 字段,Spring MVC 在将 JSON 绑定到 User 对象时,也会直接忽略这个字段。这意味着后端永远无法通过标准的数据绑定机制接收到这个值。
这在以下场景中是不可接受的:
- 用户注册/登录:前端需要发送密码,但后端不应在响应中回显密码。
- 敏感信息更新:如重置密码、修改安全邮箱等,需要接收新值但不希望旧值或新值在查询接口中泄露。
解决方案:@JsonProperty(access = …)
为了解决上述问题,Jackson 提供了 @JsonProperty 注解的 access 属性。通过设置不同的访问模式,我们可以独立控制字段的读取(序列化)和写入(反序列化)行为。
核心属性说明
| 枚举值 | 含义 | 适用场景 |
|---|---|---|
READ_WRITE |
默认值,既可读也可写 | 普通业务字段 |
READ_ONLY |
仅可读(序列化),不可写(反序列化) | 计算字段、内部状态标识 |
WRITE_ONLY |
仅可写(反序列化),不可读(序列化) | 密码、秘密令牌、一次性验证码 |
实战示例:只接收不返回
针对前文提到的密码场景,正确的做法是使用 WRITE_ONLY:
import com.fasterxml.jackson.annotation.JsonProperty;
public class UserDTO {
private String username;
/**
* WRITE_ONLY 表示:
* 1. 反序列化时:JSON 中的 "password" 会被映射到此字段。
* 2. 序列化时:此字段不会被包含在生成的 JSON 中。
*/
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
// getters and setters
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
行为验证
- 接收数据(POST /register)
- 请求体:
{"username": "john", "password": "secret123"} - 结果:
UserDTO对象的password字段成功被赋值为"secret123"。
- 请求体:
- 返回数据(GET /user/profile)
- 响应体:
{"username": "john"} - 结果:
password字段完全不出现在 JSON 中,即使对象内部持有该值。
- 响应体:
进阶建议:结合其他注解增强安全性
虽然 WRITE_ONLY 解决了基本的传输层隔离,但在实际生产中,还需注意以下几点:
1. 日志脱敏
即使 JSON 中不包含密码,如果开发者不小心将对象打印到日志中(如 log.info("Received user: {}", user)),toString() 方法可能会泄露敏感信息。建议重写 toString() 或使用专门的日志脱敏工具。
2. 数据库持久化隔离
确保在 ORM 框架(如 JPA/Hibernate)中也做了相应配置。例如,在使用 @Entity 时,如果 password 字段同时用于数据库存储,需确保其加密逻辑正确,且不要在查询所有用户列表时意外加载明文密码。
3. 区分 READ_ONLY 场景
对于某些字段,如 createdAt(创建时间),我们通常希望它在创建后由后端生成,前端不应也无法修改。此时应使用 READ_ONLY:
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private LocalDateTime createdAt;
这样,前端提交的 createdAt 将被忽略,而后端返回时会包含该字段。
总结
在 Spring Boot 中处理 JSON 字段可见性时,不要盲目使用 @JsonIgnore。@JsonIgnore 是双向屏蔽,而 @JsonProperty(access = ...) 提供了单向控制的灵活性。
- 如果需要完全隐藏字段(既不接收也不返回),使用
@JsonIgnore。 - 如果需要接收但不返回(如密码),使用
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)。 - 如果需要返回但不接收(如系统生成 ID),使用
@JsonProperty(access = JsonProperty.Access.READ_ONLY)。
掌握这些细微差别,不仅能提升 API 的安全性,还能避免许多因数据绑定失败导致的隐蔽 Bug。- - - 3. - 2. * * * 3.
更多推荐

所有评论(0)