Фильтр, который может вызывать loadUserFromUsername () через userDetailsService, чтобы получить сведения о базе данных клиента из настраиваемого экземпляра UserDetails.
Независимо от того, какой приоритет фильтра установлен, этот настраиваемый фильтр запускается перед фильтром безопасности, поэтому контекст безопасности Spring не заполнен или имеет значение NULL. Я подтвердил, что этот контекст заполняется, когда я обращаюсь к основному объекту с контроллера.
Я установил порядок безопасности Spring в application.properties на 5, и при регистрации этого фильтра я использовал большие и меньшие значения, но он всегда запускался раньше. Я знаю, что общий компонент фильтра должен позволить мне установить его в конфигурации безопасности, но я не знаю, как переместить конфигурацию и фильтр в один общий компонент фильтра.
@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() {
}
}
@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();
}
}
@fabionoth User расширяет UserDetails, и мой loadUserFromUserName возвращает User, поэтому я должен иметь возможность передать его пользователю и получить идентификатор клиента от пользователя. В настоящее время все, что я получаю из контекста безопасности, - это либо null, либо токен аутентификации usernamepasswordauthentication, который не имеет ничего, кроме имени пользователя, и даже тогда имя пользователя имеет client_id, а не конкретное имя пользователя (я использую JWT для Auth)




Нашел другой способ, который действительно хорошо работает: Аспекты!
Используемое выражение pointcut означает, что оно выполняется для всех вызовов методов из всех классов в пакете контроллеров в этом проекте.
Хранилище клиентов основано на более безопасном использовании threadlocal во избежание утечек памяти, так как таким образом оно всегда очищается (из-за блока finally)
Удачного кодирования!
@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;
}
}
}
Какие у вас данные в пользователе? после получения данных от принципала?