Frage

Ich verwende dynamisches Datenquellen-Routing, wie in diesem Blogbeitrag angegeben:http://spring.io/blog/2007/01/23/dynamic-datasource-routing/

Das funktioniert gut, aber wenn ich es kombiniere mit spring-data-rest beim Durchsuchen meiner generierten Repositorys erhalte ich (zu Recht) eine Ausnahme, dass mein Suchschlüssel nicht definiert ist (ich setze keinen Standard).

Wie und wo kann ich mich in die Spring Data Rest-Anforderungsverarbeitung einbinden, um den Suchschlüssel basierend auf 'x' (Benutzerberechtigungen, Pfadpräfix oder andere) festzulegen, bevor eine Verbindung zur Datenbank hergestellt wird?

In Bezug auf den Code stimmt meine Datenquellenkonfiguration größtenteils mit dem Blogpost oben überein, mit einigen grundlegenden Entitätsklassen, generierten Repositorys und Spring Boot, um alles zusammenzufassen.Bei Bedarf könnte ich einen Code posten, aber dort gibt es nicht viel zu sehen.

War es hilfreich?

Lösung

Meine erste Idee ist es, Spring Security zu nutzen authentication objekt zum Festlegen der aktuellen Datenquelle basierend auf authorities an die Authentifizierung angehängt.Natürlich können Sie den Suchschlüssel in eine benutzerdefinierte UserDetails objekt oder sogar ein benutzerdefiniertes Authentifizierungsobjekt.Der Kürze halber werde ich mich auf eine Lösung konzentrieren, die auf Behörden basiert.Diese Lösung erfordert ein gültiges Authentifizierungsobjekt (anonymer Benutzer kann auch eine gültige Authentifizierung haben).Abhängig von Ihrer Spring-Sicherheitskonfiguration kann das Ändern der Berechtigung / Datenquelle auf Anfrage- oder Sitzungsbasis durchgeführt werden.

Meine zweite Idee ist, mit einem zu arbeiten javax.servlet.Filter so legen Sie den Suchschlüssel in einer lokalen Thread-Variablen fest, bevor Spring Data Rest aktiviert wird.Diese Lösung ist Framework-unabhängig und kann auf Anfrage- oder Sitzungsbasis verwendet werden.

Datenquellen-Routing mit Spring Security

Verwenden SecurityContextHolder zugriff auf die Berechtigungen der aktuellen Authentifizierung.Basierend darauf entscheiden die Behörden, welche Datenquelle verwendet werden soll.Genau wie Ihr Code setze ich keine defaultTargetDataSource auf meinem 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);
    }
}

In Ihrem Code müssen Sie den Speicher ersetzen UserDetailsService (inMemoryAuthentication) mit einem UserDetailsService, der Ihren Anforderungen entspricht.Es zeigt Ihnen, dass es zwei verschiedene Benutzer mit unterschiedlichen Rollen gibt TENANT1 und TENANT2 wird für das Datenquellen-Routing verwendet.

@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();
    }
}

Hier ist ein vollständiges Beispiel: https://github.com/ksokol/spring-sandbox/tree/sdr-routing-datasource-spring-security/spring-data

Datenquellen-Routing mit Javax.ein Servlet.Filter

Erstellen Sie eine neue Filterklasse und fügen Sie sie Ihrer hinzu web.xml oder registrieren Sie es mit der AbstractAnnotationConfigDispatcherServletInitializer, jeweils.

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;
    }
}

Mandantenklasse ist ein einfacher Wrapper um eine statische 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(); }
}

Genau wie Ihr Code setze ich keine defaultTargetDataSource für meine AbstractRoutingDataSource .

public class CustomRoutingDataSource extends AbstractRoutingDataSource {

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

Jetzt können Sie die Datenquelle wechseln mit http://localhost:8080/sandbox/myEntities;tenant=tenant1.Beachten Sie, dass der Mieter bei jeder Anfrage festgelegt werden muss.Alternativ können Sie den Mandanten im HttpSession für spätere Anfragen.

Hier ist ein vollständiges Beispiel: https://github.com/ksokol/spring-sandbox/tree/sdr-routing-datasource-url/spring-data

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top