Search code examples
spring-securityspring-data-jpaspring-data

Injecting user on Spring Data JPA queries


I´ve largely used JPA and Hibernate, but I'm relativy new to Spring Data JPA. I'm trying to inject the user logged so queries get visibility restriction upon user.

Let's say we have this entity:

public class Group {
    private String code;
    private String name;
    private String creationUserId;
}

And CreationUserId is a FK to the column Id of entity User. This tries to represent that this group should only be accessed to the user whose Id equals this creationUserId.

So, taking into account I'm using JWT (the subject on the token is the userID) and springSecurity (so the user is in SecurityContextHolder.getContext().getAuthentication()), is there any fancy way of doing something like

public List<Groups> findMyGroups () {
    return groupsRepository.findMyGroups();
}

using the capabilities SpringDataJPA gives us?

Im trying to avoid doing something like

public List<Groups> findMyGroups () {
    MyUser u = (MyUser)SecurityContextHolder.getContext().getAuthentication().getDetails();
    return groupsRepository.findGroupsByUserCreationId(u.getId());
}

I don't have a problem on doing so, but it would result in such boilerplate, and maybe there is some workaround for achieving it.

Of course I don't want this on every single query, just in somes (there are two different security roles for listing, group:readAll and group:readMine).


Solution

  • Spring Security provides integration with Spring Data to achieve what you want. Take a look at the reference documentation about that.

    In short, you have to add the dependency and expose a Bean of type SecurityEvaluationContextExtension, like so:

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-data</artifactId>
    </dependency>
    
    
    @Bean
    public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
        return new SecurityEvaluationContextExtension();
    }
    

    When you have this, you can use the Authentication in your queries:

    @Query("SELECT g FROM Group g WHERE g.creationUserId = ?#{principal?.id}")
    List<Group> readMine();