13

I am trying to implement a multi-tenancy by discriminator implementation with Spring Boot and Spring Data.

I have made an abstract class to represent a multi-tenant entity. Something similar to this:

@MappedSuperclass
@FilterDefs({@FilterDef(name = "multi-tenant", parameters = {@ParamDef(name = "tenant", type = "string")})})
@Filter(name = "multi-tenant", condition = "tenant = :tenant")
public abstract class MultiTenantEntity extends GenericEntity {
    @Transient
    private transient String savedTenant;

    @PostLoad
    private void onLoad() throws Exception {
        this.savedTenant = this.tenant;
        onEntityModification();
    }

    @PrePersist
    private void onPersist() {
        if (getId() == null || getId().equals(0l)) {
            tenant = SecurityUtil.getCurrentTenant();
        }
    }

    @PreUpdate
    @PreRemove
    private void onEntityModification() throws Exception {
        String currentTenant = SecurityUtil.getCurrentTenant();

        if (!currentTenant.equals(tenant) || !savedTenant.equals(tenant)) {
            throw new Exception();
        }
    }

    @NotNull
    private String tenant;

    public String getTenant() {
        return tenant;
    }
}

How do I enable the multi-tenant hibernate filter globally?

SyntaX
  • 2,090
  • 17
  • 30
Daniel Gomes
  • 363
  • 1
  • 3
  • 9

2 Answers2

1

Using hibernate filters its easy to implement multitenantcies even for Row level ACL as well possible in our application. Instead of discrimators you could use AOP and different filters configurable in your db. Before calling your request method based on the accessing user apply the filter that is enable the hibernate session filter and exeute the request and after successfully request processing disable the filters. thats it. Using this way you could add any number of filters to any number entities that are going to be operated by the current user and you could do the perfect resource (Entity and CRUD) management with this.

Hakuna Matata
  • 755
  • 3
  • 13
  • https://stackoverflow.com/questions/22199971/discriminator-based-multi-tenancy-with-spring-data-jpahibernate/45935879#45935879 – Hakuna Matata Jun 29 '18 at 07:01
0

You can use a Spring HandlerInterceptor along with Spring's Open Session in View in order to accomplish this:

Ensure Open Session in View is enabled

application.yml

spring:
  jpa:
    open-in-view: true

Add a HandlerInterceptor

@Component
public class HibernateInterceptor implements HandlerInterceptor {

    @Autowired
    private EntityManager entityManager;

    @Override
    @Transactional
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Session session = entityManager.unwrap(Session.class);
        session.enableFilter("multi-tenant")
               .setParameter("tenant", "my-tenant-value");

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        Session session = entityManager.unwrap(Session.class);
        session.disableFilter("multi-tenant");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // This method is called after the response has been sent to the client
    }
}

Configure the HandlerInterceptor

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private HibernateInterceptor hibernateInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(hibernateInterceptor).order(Ordered.LOWEST_PRECEDENCE);
    }
}

The key is ensuring the HandlerInterceptor is run after Spring's OpenEntityManagerInViewInterceptor - this is accomplished by setting the order to Ordered.LOWEST_PRECEDENCE

mtpettyp
  • 5,533
  • 1
  • 33
  • 33