How to restrict access by role to a Spring Data RE

2019-04-06 03:10发布

问题:

In an application using Spring Data JPA and Spring Data REST, let's say you have an entity class like this:

@Entity
public class Person {

   @Id @GeneratedValue
   private int id;

   private String name;

   @JsonIgnore
   private String superSecretValue;

   ...

}

We want Spring Data REST to expose all of this entity's fields EXCEPT for superSecretValue, and so we've annotated that field with @JsonIgnore.

However, in some cases we DO want access to superSecretValue, and so we create a projection that will return all of the fields including that one:

@Projection(name = "withSecret", types = {Person.class})
public interface PersonWithSecret {

   String getName();
   String getSuperSecretValue();

}

Awesome. So now we can access Person entities including the superSecretValue field like this:

curl http://localhost:8080/persons?projection=withSecret

My question is how can we secure that projection? How can we configure things such that anyone can retrieve Person entities without the superSecretValue field... but only people with a certain role (say, ROLE_ADMIN) can use the projection to retrieve the hidden field?

I've found endless examples of using @PreAuthorize or @Secured annotations to secure Spring Data JPA repository CRUD methods (e.g. save(), delete())... but no examples of how to restrict usage of a Spring Data REST projection.

回答1:

You can overload properties in projections using @Value with conditional SpEL expressions - as in this already answered similar question.

Consider other alternatives (others already mentioned):

  1. Model refactoring. Split entity by access logic (e.g. Person <-> Account)
  2. Adding custom endpoints for special logic and access checks. For example, the current user at "/people/me".
  3. Customising standard endpoints. For example, add custom controller for "/people", "/people/{id}" that would preprocess and return custom Resource type (DTO) depending on on user authorities (e.g. returning PublicPerson instead Person). Then you can write custom resource processors for adding custom links and custom projections for these types.

See also: issue on this subject from spring-data-rest DATAREST-428.



回答2:

You could try this solution: https://stackoverflow.com/a/35399030/679240

@Projection(name = "detailed", types = User.class)
public interface UserDetailProjection extends UserSimpleProjection{

    @Value("#{@userService.checkAccess(target)? target.email : null}")
    public String getEmail();
}