6
respostas

Separação por empresa

Imagine a seguinte situação: Eu tenho uma api rest desenvolvida conforme o curso, com spring. Essa api tem o model Produto:

public class Produto {
    private Long id;
    private String descricao;
}

Na api é possível apenas para usuários autenticados fazer o get, post, put, e delete.

só que essa api vai ser usada por duas ou mais empresas diferentes que não tem vinculo nenhum entre si. e uma não poderá acessar os produtos cadastrados pelas outras e vice e versa.

A minha duvida é: O spring tem alguma ferramenta para lidar essa situação ou eu teria que programar manualmente colocando o atributo empresa no produto, e quando o usuário for fazer o get por exemplo eu pegaria a empresa que esse usuário pertence e faria um findByEmpresa no ProdutoRepository ao inves de um findAll.

EDIT: Eu pesquisei muito na internet e achei isso: https://www.baeldung.com/hibernate-5-multitenancy, mas não entendi como aplicar no projeto do curso e nem como ele trabalha tipo ele teria que identificar o usuário a quem pertence o token e fazer a consultas ao banco de dados certo, alguém pode me explicar?

6 respostas

Olá Leonardo, dê uma lida nesse artigo se te ajuda https://medium.com/swlh/multi-tenancy-implementation-using-spring-boot-hibernate-6a8e3ecb251a, mas você foi no caminho correto mesmo, essas são as duas opções.

1 - Você fazer o controle disso filtrando os dados.

2 - Você desenvolver sua aplicação multitenancy (basicamente você teria uma base centralizada contendo todos os usuários, poderia ser um micro serviço só pra fazer isso ou utilizar algo pronto, uma alternativa opensource seria o https://www.keycloak.org/), e os dados ficam em schemas separados do banco, a partir do usuário retornado você identificaria em qual schema do banco a aplicação deveria realizar as operações

Repositório exemplo: https://github.com/sumanentc/multitenant

É eu continuei pesquisando sobre multitenancy no spring, mas ainda não consigo implementar.

Esqueci de colocar o link rsrs

https://medium.com/swlh/multi-tenancy-implementation-using-spring-boot-hibernate-6a8e3ecb251a

(editei a resposta acima)

Então, continuando com essa novela aqui peguei o projeto e adicionei essas classes aqui:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TenantInterceptor());
    }
}



public class TenantInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String serverName = request.getServerName();
        String tenantId = serverName.substring(0, serverName.indexOf("."));

        request.setAttribute("tenant", tenantId);

        return true;
    }
}

Só que agora eu obtive 2 novas duvidas:

O que essas classes fazem?

A classe WebConfig implementa a interface WebMvcConfigurer, só que essa interface é para o padrão mvc e não para APIs Rest, qual é a interface que devo usar para apisRest?

Essa classe está adicionando um interceptor na sua aplicação, sempre que chegar uma requisição, antes de executar o código do seu controller irá passar pelo preHandle adicionando o atributo tenant a sua request com o valor tenantId

Desculpa ter ficado tanto tempo esse tópico aberto, mas agora que o semestre da faculdade ta acabando eu to com tempo pra retomar o estudo disso:

então eu cheguei aqui:

criei um filter, para pegar o header "empresa" da requisicão e então fazer a conexão com o banco de dados.

public class EmpresaFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String empresa = request.getHeader("empresa");
        if(empresa != null){
            System.out.println("conectar a db = " + empresa);
            // TODO: 05/07/2021 conectar a database aqui 
        }

        filterChain.doFilter(request, response);
    }
}

esse já ta configurado e funcionando, só falta fazer a conexão com o banco de dados, e é isso que eu preciso saber fazer agora.

então primeiro eu criei uma tabela no banco de dados padrao, que guarda os dados para conexao:

public class DataSourceConfig {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String nome;

    @Column(nullable = false)
    private String url;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String password;

agora falta fazer a conexao.