Pregunta

Estoy usando enrutamiento dinámico de fuente de datos como se indica en esta publicación de blog:http://spring.io/blog/2007/01/23/dynamic-datasource-routing/

Esto funciona bien, pero cuando lo combino con spring-data-rest y al explorar mis repositorios generados, obtengo (con razón) una excepción de que mi clave de búsqueda no está definida (no establezco un valor predeterminado).

¿Cómo y dónde puedo conectarme al manejo de solicitudes de resto de datos de Spring para configurar la clave de búsqueda basada en 'x' (autorizaciones de usuario, prefijo de ruta u otro), antes de realizar cualquier conexión a la base de datos?

En cuanto al código, mi configuración de fuente de datos coincide principalmente con la publicación del blog en la parte superior, con algunas clases de entidad básicas, repositorios generados y Spring Boot para envolver todo.Si es necesario, podría publicar algún código, pero no hay mucho que ver allí.

¿Fue útil?

Solución

Mi primera idea es aprovechar la tecnología de Spring Security. authentication objeto para establecer la fuente de datos actual en función de authorities adjunto a la autenticación.Por supuesto, puedes poner la clave de búsqueda en un archivo personalizado. UserDetails objeto o incluso un objeto de autenticación personalizado también.En aras de la brevedad, me concentraré en una solución basada en autoridades.Esta solución requiere un objeto de autenticación válido (el usuario anónimo también puede tener una autenticación válida).Dependiendo de su configuración de Spring Security, el cambio de autoridad/fuente de datos se puede realizar por solicitud o sesión.

Mi segunda idea es trabajar con un javax.servlet.Filter para establecer la clave de búsqueda en una variable local de hilo antes de que se active Spring Data Rest.Esta solución es independiente del marco y se puede utilizar por solicitud o sesión.

Enrutamiento de fuentes de datos con Spring Security

Usar SecurityContextHolder para acceder a las autoridades de autenticación actuales.Las autoridades deciden qué fuente de datos utilizar.Al igual que su código, no estoy configurando un defaultTargetDataSource en mi AbstractRoutingDataSource.

public class CustomRoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        Set<String> authorities = getAuthoritiesOfCurrentUser();
        if(authorities.contains("ROLE_TENANT1")) {
            return "TENANT1";
        }
        return "TENANT2";
    }

    private Set<String> getAuthoritiesOfCurrentUser() {
        if(SecurityContextHolder.getContext().getAuthentication() == null) {
            return Collections.emptySet();
        }
        Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
        return AuthorityUtils.authorityListToSet(authorities);
    }
}

En tu código debes reemplazar el en memoria. UserDetailsService (inMemoryAuthentication) con un UserDetailsService que satisfaga sus necesidades.Te muestra que hay dos usuarios diferentes con roles diferentes. TENANT1 y TENANT2 utilizado para el enrutamiento de la fuente de datos.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
            .withUser("user1").password("user1").roles("USER", "TENANT1")
            .and()
            .withUser("user2").password("user2").roles("USER", "TENANT2");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/**")
            .authorizeRequests()
            .antMatchers("/**").hasRole("USER")
            .and()
            .httpBasic()
            .and().csrf().disable();
    }
}

Aquí tienes un ejemplo completo: https://github.com/ksokol/spring-sandbox/tree/sdr-routing-datasource-spring-security/spring-data

Enrutamiento de fuente de datos con javax.servlet.Filter

Cree una nueva clase de filtro y agréguela a su web.xml o registrarlo en el AbstractAnnotationConfigDispatcherServletInitializer, respectivamente.

public class TenantFilter implements Filter {

    private final Pattern pattern = Pattern.compile(";\\s*tenant\\s*=\\s*(\\w+)");

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String tenant = matchTenantSystemIDToken(httpRequest.getRequestURI());
        Tenant.setCurrentTenant(tenant);
        try {
            chain.doFilter(request, response);
        } finally {
            Tenant.clearCurrentTenant();
        }
    }

    private String matchTenantSystemIDToken(final String uri) {
        final Matcher matcher = pattern.matcher(uri);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }
}

La clase Tenant es un contenedor simple alrededor de una clase estática. ThreadLocal.

public class Tenant {

    private static final ThreadLocal<String> TENANT = new ThreadLocal<>();

    public static void setCurrentTenant(String tenant) { TENANT.set(tenant); }

    public static String getCurrentTenant() { return TENANT.get(); }

    public static void clearCurrentTenant() { TENANT.remove(); }
}

Al igual que su código, no estoy configurando un defaultTargetDataSource en mi AbstractRoutingDataSource.

public class CustomRoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        if(Tenant.getCurrentTenant() == null) {
            return "TENANT1";
        }
        return Tenant.getCurrentTenant().toUpperCase();
    }
}

Ahora puedes cambiar la fuente de datos con http://localhost:8080/sandbox/myEntities;tenant=tenant1.Tenga en cuenta que el inquilino debe configurarse en cada solicitud.Alternativamente, puede guardar al inquilino en el HttpSession para solicitudes posteriores.

Aquí tienes un ejemplo completo: https://github.com/ksokol/spring-sandbox/tree/sdr-routing-datasource-url/spring-data

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top