質問

このブログ投稿で示されているように、私は動的データソース ルーティングを使用しています。http://spring.io/blog/2007/01/23/dynamic-datasource-routing/

これはうまく機能しますが、それを組み合わせると、 spring-data-rest 生成されたリポジトリを参照すると、ルックアップキーが定義されていない(デフォルトを設定していない)という例外が(当然のことながら)発生します。

データベースへの接続が確立される前に、どこで Spring データ レスト リクエスト処理にフックして、「x」 (ユーザー認証、パス プレフィックスなど) に基づいてルックアップ キーを設定できますか?

コード的には、私のデータソース構成は上部のブログ投稿とほとんど一致しており、いくつかの基本的なエンティティクラス、生成されたリポジトリ、およびすべてをまとめるための Spring Boot が含まれています。必要に応じてコードを投稿することもできますが、そこには見るべきものは何もありません。

役に立ちましたか?

解決

私の最初のアイデアは、Spring Security を活用することです。 authentication 現在のデータソースに基づいて設定するオブジェクト authorities 認証に付属します。もちろん、カスタムキーにルックアップキーを入れることもできます。 UserDetails オブジェクト、またはカスタムの Authentication オブジェクトも使用できます。簡潔にするために、当局に基づいた解決策に焦点を当てます。このソリューションには有効な認証オブジェクトが必要です (匿名ユーザーも有効な認証を持つことができます)。Spring Security の構成に応じて、権限/データソースの変更はリクエストごとまたはセッションごとに実行できます。

私の 2 番目のアイデアは、 javax.servlet.Filter Spring Data Rest が起動する前に、スレッド ローカル変数にルックアップ キーを設定します。このソリューションはフレームワークに依存せず、リクエストごとまたはセッションごとに使用できます。

Spring Security を使用したデータソース ルーティング

使用 SecurityContextHolder 現在の認証の権限にアクセスします。当局に基づいて、どのデータソースを使用するかを決定します。あなたのコードと同じように、私はdefaultTargetDataSourceを設定していません 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);
    }
}

コード内でメモリ内の を置き換える必要があります UserDetailsService (inMemoryAuthentication) のニーズを満たす UserDetailsS​​ervice を使用します。異なる役割を持つ 2 人の異なるユーザーがいることを示しています。 TENANT1 そして TENANT2 データソースのルーティングに使用されます。

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

完全な例を次に示します。 https://github.com/ksokol/spring-sandbox/tree/sdr-routing-datasource-spring-security/spring-data

javax.servlet.Filter を使用したデータソースのルーティング

新しいフィルター クラスを作成し、 web.xml または、 AbstractAnnotationConfigDispatcherServletInitializer, 、 それぞれ。

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

テナント クラスは静的クラスの単純なラッパーです。 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(); }
}

あなたのコードと同じように、私はAbstractRoutingDataSourceにdefaultTargetDataSourceを設定していません。

public class CustomRoutingDataSource extends AbstractRoutingDataSource {

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

これで、データソースを切り替えることができます http://localhost:8080/sandbox/myEntities;tenant=tenant1. 。リクエストごとにテナントを設定する必要があることに注意してください。または、テナントを HttpSession 後続のリクエストのために。

完全な例を次に示します。 https://github.com/ksokol/spring-sandbox/tree/sdr-routing-datasource-url/spring-data

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top