2017-02-14 11 views
0

根據Spring Data REST Documentation,POST方法會根據給定的請求主體創建一個新的實體。但是,我發現它也可以更新現有的實體。在某些情況下,這可能會有問題。這裏有一個例子:春季數據REST中不會導致PK衝突的POST重複條目

DemoApplication.java

package com.example; 

import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 

@SpringBootApplication 
public class DemoApplication { 

    public static void main(String[] args) { 
     SpringApplication.run(DemoApplication.class, args); 
    } 
} 

UserRepository.java

package com.example; 

import org.springframework.data.repository.PagingAndSortingRepository; 

public interface UserRepository extends PagingAndSortingRepository<User, String> {} 

User.java

package com.example; 

import javax.persistence.Entity; 
import javax.persistence.Id; 

@Entity 
public class User { 
    @Id 
    private String username; 
    private String password; 
    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; 
    } 
} 

的pom.xml(內項目標籤)

<modelVersion>4.0.0</modelVersion> 

<groupId>com.example</groupId> 
<artifactId>demo</artifactId> 
<version>0.0.1-SNAPSHOT</version> 
<packaging>jar</packaging> 

<name>demo</name> 
<description>Demo project for Spring Boot</description> 

<parent> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-parent</artifactId> 
    <version>1.5.1.RELEASE</version> 
    <relativePath/> <!-- lookup parent from repository --> 
</parent> 

<properties> 
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 
    <java.version>1.8</java.version> 
</properties> 

<dependencies> 
    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-data-jpa</artifactId> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-data-rest</artifactId> 
    </dependency> 

    <dependency> 
     <groupId>com.h2database</groupId> 
     <artifactId>h2</artifactId> 
     <scope>runtime</scope> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-test</artifactId> 
     <scope>test</scope> 
    </dependency> 
</dependencies> 

<build> 
    <plugins> 
     <plugin> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-maven-plugin</artifactId> 
     </plugin> 
    </plugins> 
</build> 

application.properties:空

URL:http://localhost:8080/users

方法:POST

JSON內容:

{"username":"user","password":"password"} 

我假定上述POST請求被在第一時間獲取HTTP 201 ,只有一次。但是,我能夠多次發送上述POST請求,並始終獲得HTTP 201。另外,我還可以使用POST請求更改數據庫中的密碼。

我認爲這是一個安全問題。例如,我可能允許通過POST請求進行匿名用戶註冊。但是,在上述情況下,現有用戶可能會被覆蓋。

問:我如何防止POST請求創建一個新的實體,如果舊的實體已經用相同的ID存在?或者,我是否錯過了解釋Spring Data REST文檔?

補充說明:

這個問題的原因是Spring背後的數據REST設計。因爲Spring Data REST是建立在Spring Data JPA的基礎上的,而Spring Data JPA並不是用來直接暴露給「外部」的。因此它「信任」數據。org.springframework.data.repository.core.support.AbstractEntityInformation中的方法isNew顯示數據如何確定爲新數據或不新數據。

public boolean isNew(T entity) { 

    ID id = getId(entity); 
    Class<ID> idType = getIdType(); 

    if (!idType.isPrimitive()) { 
     return id == null; 
    } 

    if (id instanceof Number) { 
     return ((Number) id).longValue() == 0L; 
    } 

    throw new IllegalArgumentException(String.format("Unsupported primitive id type %s!", idType)); 
} 

isNew方法的結果最終會影響org.springframework.data.jpa.repository.support.SimpleJpaRepository中的存儲方法。

public <S extends T> S save(S entity) { 

    if (entityInformation.isNew(entity)) { 
     em.persist(entity); 
     return entity; 
    } else { 
     return em.merge(entity); 
    } 
} 

在這個問題中提及的情況下,用戶名字段,這也是用戶實體的ID,將始終包含以創建新的用戶數據。因此,當它進入isNew時,id == null將始終返回false。然後,save方法將始終執行合併操作。

上述提示是我所能提供的。儘管如此,我不知道是否有解決方案來解決這個問題。

URL鏈接只是參考。它們可能與我使用的不完全相同。

+1

即使它不是PK/ID,在添加/編輯之前仍需要驗證才能捕獲重複項。對?所以寫一個驗證器。 http://www.baeldung.com/spring-data-rest-validators –

回答

0

爲了使實體能夠與Spring Data REST(以及Spring Data JPA)一起正常工作,實體類需要實現Persistable。更重要的方法是isNew()。此方法將被調用,而不是在問題中提到的AbstractEntityInformation中。爲了使實體知道自己的狀態(新的或舊的),還需要一個版本變量。通過在顯式字段上註釋@Version,Spring Data JPA將更新此字段。因此,一旦實體第一次構建,該字段就是默認值(null或0,取決於它使用的數據類型)。另外,由於Spring Data REST旨在向外界公開,爲了防止版本被濫用,@JsonIgnore用於版本字段。

對於這個特定的問題,User.java類需要被改變爲如下:

package com.example; 

import javax.persistence.*; 
import org.springframework.data.domain.Persistable; 
import com.fasterxml.jackson.annotation.JsonIgnore; 

@Entity 
public class User implements Persistable<String> { 

    /** 
    * 
    */ 
    private static final long serialVersionUID = 7509971300023426574L; 
    @Id 
    private String username; 

    private String password; 

    @Version 
    @JsonIgnore 
    private Long version; 

    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; 
    } 

    public Long getVersion() { 
     return version; 
    } 

    public void setVersion(Long version) { 
     this.version = version; 
    } 

    @Override 
    public String getId() { 
     return username; 
    } 

    @Override 
    public boolean isNew() { 
     return version == null; 
    } 
} 

如@Alan乾草提到的,驗證也應該用於輸入數據的執行。這絕對是好事。

相關問題