Доступ к пользовательским деталям из фильтра (Spring) - PullRequest
0 голосов
/ 31 мая 2018

Спецификация

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

Проблема

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

Попытки

Я установил порядок безопасности 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();
    }
}

1 Ответ

0 голосов
/ 03 июня 2018

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

Используемое выражение 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;
        }
    }
}
...