1

我正在使用带有 Java 11 的 Spring Boot 2.1。我已经用 fastxml 注释对我的用户模型进行了注释,以便我的密码可以被 POST 请求接受,但不能被其他 REST 请求返回......

@Data
@Entity
@Table(name = "Users")
public class User implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;
    
    
    private String firstName;
    private String lastName;
    
    @NotBlank(message = "Email is mandatory")
    @Column(unique=true)
    private String email;
    
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private String password;
    private boolean enabled;
    private boolean tokenExpired;
 
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable( 
        name = "users_roles", 
        joinColumns = @JoinColumn(
          name = "user_id", referencedColumnName = "id"), 
        inverseJoinColumns = @JoinColumn(
          name = "role_id", referencedColumnName = "id")) 
    private Collection<Role> roles;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getUsername() {
        return email;
    }

    @Override
    public boolean isAccountNonExpired() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        // TODO Auto-generated method stub
        return false;
    }    

    @PrePersist @PreUpdate 
    private void prepare(){
        this.email = this.email.toLowerCase();
    }
}

但是,在尝试运行集成测试时,“objectMapper.writeValueAsString”不会翻译密码。这是我的测试...

@SpringBootTest(classes = CardmaniaApplication.class, 
webEnvironment = WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {
    
    @Autowired
private TestRestTemplate restTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private IUserRepository userRepository;

    @Test
    @WithUserDetails("me@example.com")
    void registrationWorksThroughAllLayers() throws Exception {
        final String email = "newuser@test.com";
        final String firstName = "first";
        final String lastName = "last";
        final String password = "password";
        User user = getTestUser(email, password, firstName, lastName, Name.USER);
    
        ResponseEntity<String> responseEntity = this.restTemplate
            .postForEntity("http://localhost:" + port + "/api/users", user, String.class);
    assertEquals(201, responseEntity.getStatusCodeValue());

        final User createdUser = userRepository.findByEmail(email);
        assertNotNull(createdUser);
        assertNotNull(createdUser.getPassword());
    }

    @Test
    @WithUserDetails("me@example.com")
    void getDetailsAboutMyself() throws JsonProcessingException, JSONException {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        UserDetails user = (UserDetails) authentication.getPrincipal();
        final User foundUser = userRepository.findByEmail(user.getUsername());
        ResponseEntity<String> responseEntity = this.restTemplate
            .getForEntity("http://localhost:" + port + "/api/users/" + foundUser.getId(), String.class);
        assertEquals(200, responseEntity.getStatusCodeValue());
        // assert proper response
        final String userAsJson = objectMapper.writeValueAsString(user);
        assertEquals(userAsJson, responseEntity.getBody());
        JSONObject object = (JSONObject) new JSONTokener(userAsJson).nextValue();
        // Verify no password is returned.
        assertNull(object.getString("password"));
    }
    ...
}

objectMapper.writeValueAsString 调用中的 JSON 是

{"id":null,"firstName":"first","lastName":"last","email":"newuser@test.com","enabled":true,"tokenExpired":false,"roles":null,"username":"newuser@test.com","authorities":null,"accountNonExpired":false,"accountNonLocked":false,"credentialsNonExpired":false}

在从读取端点请求我的实体时,将我的密码包含在映射中以及抑制密码的正确方法是什么?

4

2 回答 2

0

这是一个常见的误解,甚至有一个错误报告,他们澄清了文档

“READ”和“WRITE”是从Java Property的角度来理解的,即当你序列化一个Object时,你必须读取它的属性,而当你反序列化它时,你必须它。

在你的情况下,你想要@JsonProperty(access = JsonProperty.Access.READ_ONLY)

于 2020-06-29T19:36:03.383 回答
0
  • 在您的情况下使用WRITE_ONLYorREAD_ONLY将不起作用。原因是通过 http 调用两者都需要。让我们this.restTemplate.postForEntity举个例子。在发送端,您的Userjava 对象需要序列化为 json,因此它需要 .READ当其余端点接收到 json 时,它需要将 json 反序列化为Userjava 对象,因此需要WRITE. this.restTemplate.getForEntity这也将是相同的情况

  • 一种解决方案是在返回之前将端点password上的 User 字段设置GET为 null

  • 另一种解决方案是创建一个单独的UserDto无密码字段并从GET端点返回

  • 另一种解决方案是创建两个JsonViews,其中一个带有密码,另一个没有密码。然后用正确的注释你的端点@JsonView

于 2020-07-13T09:47:47.553 回答