2017-02-14 26 views
0

Согласно Spring Data REST Documentation, метод POST создает новый объект из данного тела запроса. Однако я обнаружил, что он может также обновить существующий объект. В некоторых случаях это может быть проблематично. Вот пример:POST повторяющаяся запись, не вызывающая столкновение PK в Spring Data REST

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, если старый объект уже существует с тем же идентификатором? Или я пропустил интерпретацию документа REST Spring Data?

Дополнительные пояснения:

Причиной этой проблемы является разработка позади Spring Data REST. Поскольку Spring Data REST построена на Spring Data JPA, который не использовался для непосредственного отображения на «вне». Таким образом, он «доверяет» данным, которые поступают. Метод isNew в org.springframework.data.repository.core.support.AbstractEntityInformation показывает, как данные определяются как новые или не новые.

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

В ситуации, упомянутой в этом вопросе, поле имени пользователя, которое также идентификатор объекта пользователя, всегда будет содержать данные для создания новых пользователей. Поэтому, когда он переходит к isNew, id == null всегда возвращает false. Тогда метод save всегда будет выполнять операцию слияния.

Указанные выше подсказки - все, что я могу предоставить. Несмотря на это, я не знаю, есть ли решение для решения этой проблемы.

Ссылки на ссылки - это всего лишь ссылки. Они могут быть не точной версией того, что я использую.

+1

Даже если это не PK/ID, вам все равно придется проверять до добавления/редактирования, чтобы поймать дубликаты. Правильно? Поэтому напишите валидатор. http://www.baeldung.com/spring-data-rest-validators –

ответ

0

Чтобы заставить объект работать должным образом с помощью REST данных Spring (и 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 Ай, проверка также должна быть выполнена для поступающих данных. Это определенно хорошо.

 Смежные вопросы

  • Нет связанных вопросов^_^