Spring Data JPA使用@Query查询时返回未请求的敏感数据原因及解决办法
问题原因
- 你的Repository查询返回类型是
Post实体类,JPA执行原生SQL后,仅会用返回的user_id填充Post的user关联属性。user属性配置了FetchType.LAZY懒加载,当你后续访问user的任意属性(比如接口序列化返回时自动调用getUser()的getter方法),JPA会自动触发懒加载查询完整的User对象所有字段,包含密码等敏感数据。 - 你原生SQL中查询的
b.username没有对应的映射规则,JPA不会自动将这个字段赋值给Post.user.username,相当于这个查询出来的字段被直接丢弃了。
修复方案
方案1:使用DTO投影(最推荐)
自定义仅包含所需字段的传输对象,不直接返回实体类,从根源避免敏感字段泄露。
步骤1:定义PostDTO
@Data @AllArgsConstructor public class PostDTO { private Integer id; private String city; private String food; private String time; private Integer userId; private String username; }
步骤2:修改Repository方法(改用JPQL更简洁)
@Repository public interface PostRepository extends JpaRepository<Post, Integer> { /** * 查询所有帖子 * @return */ @Query("SELECT new 你的DTO全类名.PostDTO(p.id, p.city, p.food, p.time, p.user.id, p.user.username) FROM Post p ORDER BY p.id DESC") List<PostDTO> findAllByOrderByIdDesc(); /** * 根据城市搜索查询所有帖子 * @param city * @return */ @Query("SELECT new 你的DTO全类名.PostDTO(p.id, p.city, p.food, p.time, p.user.id, p.user.username) FROM Post p WHERE UPPER(p.city) LIKE CONCAT('%',UPPER(:city),'%') ORDER BY p.id DESC") List<PostDTO> findAllByCityOrderByIdDesc(@Param("city") String city); }
该方案不会触发额外的懒加载查询,仅会查询你指定的字段,安全性最高。
方案2:敏感字段加序列化忽略注解(临时方案)
如果不方便修改返回结构,可以在User实体的敏感字段上添加序列化忽略注解,避免接口返回时泄露敏感数据:
@Entity @Data @Table(name="user", uniqueConstraints={@UniqueConstraint(columnNames = {"email"})}) public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; @Column(nullable = false) @JsonIgnore // 序列化时忽略该字段 private String email; @Column(nullable = false) private String username; @Column(nullable = false) @JsonIgnore // 序列化时忽略该字段 private String password; }
注意该方案只是序列化时隐藏敏感字段,JPA依然会查询到完整的User对象,只是不会返回给前端,属于治标不治本的方案。
方案3:原生SQL配合结果集映射
如果你坚持要使用原生SQL,可以配置结果集映射和DTO配合使用:
步骤1:在Post实体上添加结果集映射配置
@Entity @Data @SqlResultSetMapping( name = "PostDTOMapping", classes = @ConstructorResult( targetClass = PostDTO.class, columns = { @ColumnResult(name = "id", type = Integer.class), @ColumnResult(name = "city", type = String.class), @ColumnResult(name = "food", type = String.class), @ColumnResult(name = "time", type = String.class), @ColumnResult(name = "user_id", type = Integer.class), @ColumnResult(name = "username", type = String.class) } ) ) public class Post { // 原有实体属性不变 }
步骤2:修改Repository的原生查询配置
@Query(value="SELECT a.id, a.food, a.time, a.city, a.user_id, b.username FROM POST a INNER JOIN USER b ON a.user_id = b.id ORDER BY a.id DESC", nativeQuery = true, resultSetMapping = "PostDTOMapping") List<PostDTO> findAllByOrderByIdDesc();
内容的提问来源于stack exchange,提问作者Daekun Han
相关产品推荐
相关产品推荐

