Spring Data Rest использует только путь с родителем - PullRequest
2 голосов
/ 30 апреля 2019

Краткое описание: Я хочу, чтобы spring-data-rest генерировал только конечные точки, такие как "/ users / {userId} / settings", "/ users / {userId} / values", а не "/ settings" или "/ values" .

Подробнее:

Я начал раздражаться проверкой в ​​каждой функции контроллера, имеет ли зарегистрированный пользователь доступ к запрошенному ресурсу и имел следующую идею для его автоматизации (если у кого-то есть идея получше, которая не вызывает проблем ниже, я был бы рад услышать)

В моем приложении пользователь имеет доступ только к своему ресурсу и не может видеть данные других пользователей.

Поэтому моя идея заключалась в том, чтобы ограничить (с помощью конфигурации Websecurity) все пути, начинающиеся с "/ users / {id} **", чтобы разрешить доступ только тогда, когда вошедший в систему пользователь имеет указанный идентификатор. Таким образом, если мои конечные точки, например, "/ пользователей / {USERID} / userValues" или "/ users / {userId} / settings" и т. д. каждый пользователь может получить доступ только к своим данным из-за правила разрешения

Для дальнейшей автоматизации процесса и обеспечения того, чтобы я не забывал проверку о userId в контроллере, я попытался использовать spring-data-rest, что прекрасно, за исключением одной проблемы:

spring-data-rest генерирует API-конечную точку для каждого объекта. Они выглядят так:
"/ settings", "/ users", "/ values".

Пути, которые я хочу, также генерируются: "/ users / {userId} / values" и "/ users / {userId} / values", и он корректно проверяет userId, поэтому моя идея, кажется, работает в принципе.

Однако я не могу понять, как отключить конечные точки, где путь начинается с дочернего элемента ("/ settings", "/ values".), Поскольку я хочу только конечные точки, начинающиеся с "/ users / {userId} "быть доступным.

Возможно ли это? Или есть лучший способ добиться того, чтобы пользователи могли видеть и редактировать только свои собственные данные?

Вот классы моего проекта для тестирования этого сценария (я знаю, что наименование ужасно, я настрою новый проект, если я знаю, что могу сделать эту работу, это только для тестирования):

Зависимости Gradle:

dependencies {
    compile("org.springframework.boot:spring-boot-starter")
    //web frontend
    compile("org.springframework.boot:spring-boot-starter-web")
    //rest endpoint generator
    compile("org.springframework.boot:spring-boot-starter-data-rest")
    //database connection
    compile("mysql:mysql-connector-java")
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    //Swagger (/v2/api-docs)
    compile("io.springfox:springfox-swagger2:2.9.2")
    //pretty swagger (/swagger-ui.html)
    compile("io.springfox:springfox-swagger-ui:2.9.2")
    //Lombok (automatic getter / setter)
    compile("org.projectlombok:lombok:1.18.6")
}

Пользователь:

@Entity
@Getter
@Setter
public class User {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Integer id;

  private String firstName;

  private String lastName;

  @OneToMany(mappedBy="user", cascade= CascadeType.ALL)
  private List<UserValue> values;

  @OneToOne
  @RestResource(path = "settings", rel = "SettingsRel")
  private Settings settings;
}

UserValue:

@Entity
public class UserValue {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  Integer id;

  String value;

  @ManyToOne
  @JoinColumn(name = "user_id")
  @JsonIgnore
  private User user;

  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  public String getValue() {
    return value;
  }

  public void setValue(String value) {
    this.value = value;
  }

  public User getUser() {
    return user;
  }

  public void setUser(User user) {
    this.user = user;
  }
}

Настройки:

@Entity
@Getter
@Setter
public class Settings {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  Integer id;

  private HashMap<String, Object> settings;
}

UserValueRepository:

public interface UserValueRepository extends CrudRepository<UserValue, Integer> {
}

UserRepository:

   public interface UserRepository extends CrudRepository<User, Integer> {
    }

SettingsRepository:

  public interface SettingsRepository extends CrudRepository<Settings, Integer> {
    }

Две дополнительные проблемы, с которыми я сталкиваюсь с spring-data-rest, заключаются в том, что он, похоже, не работает с Swagger и Lombok. Так что я еще более открыт для альтернативных способов достижения своей цели

1 Ответ

0 голосов
/ 05 мая 2019

Что касается моего исходного вопроса: есть два способа добиться отключения путей, которые не начинаются с «/ users / {userId}»: либо отключить его непосредственно в WebSecurityConfigurerAdapter, либо использовать фильтр.Я использовал фильтр, который перенаправлял, например, запрос "/ settings" в "/ users / {idOfLoggedInUser} / settings", и все это прекрасно работало для GET-запроса.

Однако кажется, что spring-data-restобрабатывает только запросы POST и PUT для корневого пути объекта, поэтому пока GET ("/ users / {userId} / settings") работает без проблем, если вы хотите выполнить POST, он должен быть "/ settings" и можетне будет автоматически привязан к пользователю.

Так что, похоже, мне все-таки нужно написать контроллер вручную.

Однако я все же нашел более элегантное решение основной проблемы аутентификации, чем вручнуювыберите правильного пользователя из базы данных и затем установите этого пользователя для данного объекта с помощью @PreAuthorize и @PostAuthorize в Spring.Обе концепции все еще немного сложны для меня, но они могут выполнить свою работу и впоследствии будут очень хорошо расширяться для правил, таких как «друзья также могут видеть данные, если вы предоставили им доступ» или подобные вещи.

...