规格说明
一个过滤器,它可以通过loadUserFromUsername()调用userDetailsService,以便从自定义UserDetails实例中检索租户DB详细信息。
问题
不管过滤器优先级设置为什么,这个自定义过滤器在安全过滤器之前运行,因此spring安全上下文是未填充或空的。当我从控制器访问主体对象时,我已经确认了这个上下文是填充的。
尝试
我已经将application.properties中的spring安全顺序设置为5,在注册此筛选器时,我使用了越来越小的值,但它一直运行在前面。我知道泛型过滤器bean应该允许我在安全配置中设置它,但是我不知道如何将配置和过滤器移动到一个通用过滤器bean中。
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();
}
}
发布于 2018-06-02 13:57:52
找到了另一种很好的工作方式:方面!
所使用的切入点表达式意味着该项目中控制器包中所有类的所有方法调用都会运行。
租户存储的基础是更安全地使用线程本地,以避免内存泄漏,因为它总是被清除(由于最后的块)。
编码愉快!
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;
}
}
}
https://stackoverflow.com/questions/50615234
复制