Доступ к UserDetails из фильтра (Spring)

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

Фильтр, который может вызывать 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();
    }
}

Какие у вас данные в пользователе? после получения данных от принципала?

Fabio Noth 31.05.2018 03:30

@fabionoth User расширяет UserDetails, и мой loadUserFromUserName возвращает User, поэтому я должен иметь возможность передать его пользователю и получить идентификатор клиента от пользователя. В настоящее время все, что я получаю из контекста безопасности, - это либо null, либо токен аутентификации usernamepasswordauthentication, который не имеет ничего, кроме имени пользователя, и даже тогда имя пользователя имеет client_id, а не конкретное имя пользователя (я использую JWT для Auth)

Jordan Mackie 31.05.2018 09:54
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
2
1 462
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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;
        }
    }
}

Другие вопросы по теме