Технические характеристики

Фильтр, который может вызывать loadUserFromUsername () через userDetailsService, чтобы получить сведения о базе данных клиента из настраиваемого экземпляра UserDetails.

Проблема

Независимо от того, какой приоритет фильтра установлен, этот настраиваемый фильтр запускается перед фильтром безопасности, поэтому контекст безопасности Spring не заполнен или имеет значение NULL. Я подтвердил, что этот контекст заполняется, когда я обращаюсь к основному объекту с контроллера.

Попытки

Я установил порядок безопасности Spring в application.properties на 5, и при регистрации этого фильтра я использовал большие и меньшие значения, но он всегда запускался раньше. Я знаю, что общий компонент фильтра должен позволить мне установить его в конфигурации безопасности, но я не знаю, как переместить конфигурацию и фильтр в один общий компонент фильтра.

TenantFilter.java

@Component
public class TenantFilter implements Filter {

    @Autowired
    private TenantStore tenantStore;

    @Autowired
    private UserService userService;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;

        User user = null;
        try {
            user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        } catch (UsernameNotFoundException ignored) {}

        String tenantId = user != null ? user.getSchool().getCode() : "";

        try {
            this.tenantStore.setTenantId(tenantId);
            chain.doFilter(servletRequest, servletResponse);
        } finally {
            // Otherwise when a previously used container thread is used, it will have the old tenant id set and
            // if for some reason this filter is skipped, tenantStore will hold an unreliable value
            this.tenantStore.clear();
        }
    }

    @Override
    public void destroy() {

    }
}

TenantFilterConfig.java

@Configuration
public class TenantFilterConfig {

    @Bean
    public Filter tenantFilter() {
        return new TenantFilter();
    }

    @Bean
    public FilterRegistrationBean tenantFilterRegistration() {
        FilterRegistrationBean result = new FilterRegistrationBean();
        result.setFilter(this.tenantFilter());
        result.setUrlPatterns(Lists.newArrayList("/*"));
        result.setName("Tenant Store Filter");
        result.setOrder(Ordered.LOWEST_PRECEDENCE-1);
        return result;
    }

    @Bean(destroyMethod = "destroy")
    public ThreadLocalTargetSource threadLocalTenantStore() {
        ThreadLocalTargetSource result = new ThreadLocalTargetSource();
        result.setTargetBeanName("tenantStore");
        return result;
    }

    @Primary
    @Bean(name = "proxiedThreadLocalTargetSource")
    public ProxyFactoryBean proxiedThreadLocalTargetSource(ThreadLocalTargetSource threadLocalTargetSource) {
        ProxyFactoryBean result = new ProxyFactoryBean();
        result.setTargetSource(threadLocalTargetSource);
        return result;
    }

    @Bean(name = "tenantStore")
    @Scope(scopeName = "prototype")
    public TenantStore tenantStore() {
        return new TenantStore();
    }
}
0
Jordan Mackie 31 Май 2018 в 03:59

1 ответ

Лучший ответ

Нашел другой способ, который действительно хорошо работает: Аспекты!

Используемое выражение pointcut означает, что оно выполняется для всех вызовов методов из всех классов в пакете контроллеров в этом проекте.

Хранилище клиентов основано на более безопасном использовании threadlocal во избежание утечек памяти, так как таким образом оно всегда очищается (из-за блока finally)

Удачного кодирования!

TenantAspect.java

@Component
@Aspect
public class TenantAspect {

    private final
    TenantStore tenantStore;

    @Autowired
    public TenantAspect(TenantStore tenantStore) {
        this.tenantStore = tenantStore;
    }

    @Around(value = "execution(* com.things.stuff.controller..*(..))")
    public Object assignForController(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        return assignTenant(proceedingJoinPoint);
    }

    private Object assignTenant(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        try {
            User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            if (user != null) tenantStore.setTenantId(user.getSchool().getCode());
        } finally {
            Object retVal;
            retVal = proceedingJoinPoint.proceed();
            tenantStore.clear();
            return retVal;
        }
    }
}
1
Jordan Mackie 2 Июн 2018 в 21:57