문제

저는 이 블로그 게시물에 표시된 대로 동적 데이터 소스 라우팅을 사용하고 있습니다.http://spring.io/blog/2007/01/23/dynamic-datasource-routing/

이것은 잘 작동하지만 다음과 결합하면 spring-data-rest 생성된 저장소를 검색하면 조회 키가 정의되지 않았다는 예외가 발생합니다(기본값을 설정하지 않음).

데이터베이스에 연결하기 전에 'x'(사용자 인증, 경로 접두사 등)를 기반으로 조회 키를 설정하기 위해 Spring 데이터 나머지 요청 처리에 어떻게, 어디서 연결할 수 있습니까?

코드 측면에서 내 데이터 소스 구성은 일부 기본 엔터티 클래스, 생성된 리포지토리 및 모든 것을 하나로 묶는 Spring Boot를 포함하여 상단의 블로그 게시물과 대부분 일치합니다.필요한 경우 일부 코드를 게시할 수 있지만 거기에는 별로 볼 것이 없습니다.

도움이 되었습니까?

해결책

나의 첫 번째 아이디어는 Spring Security의 기능을 활용하는 것입니다. authentication 현재 데이터 소스를 설정하는 객체 authorities 인증에 첨부합니다.물론 조회 키를 사용자 정의에 넣을 수도 있습니다. UserDetails 개체 또는 사용자 정의 인증 개체도 마찬가지입니다.간결함을 위해 당국에 기반한 솔루션에 집중하겠습니다.이 솔루션에는 유효한 인증 개체가 필요합니다(익명 사용자도 유효한 인증을 가질 수 있음).Spring Security 구성에 따라 권한/데이터 소스 변경은 요청별로 또는 세션별로 수행될 수 있습니다.

두 번째 아이디어는 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)을 사용자의 필요에 맞는 UserDetailsService로 사용하세요.서로 다른 역할을 가진 두 명의 서로 다른 사용자가 있음을 보여줍니다. 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