从筛选器访问 UserDetails (Spring)
Accessing UserDetails from Filter (Spring)
规格
一个过滤器,可以通过 userDetailsService 调用 loadUserFromUsername(),以便从自定义 UserDetails 实例中检索租户数据库详细信息。
问题
无论过滤器优先级设置为什么,此自定义过滤器都会在安全过滤器之前运行,因此 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();
}
}
找到了一种非常有效的不同方式:Aspects!
所使用的切入点表达式意味着它会围绕该项目中控制器包中所有 类 的所有方法调用运行。
租户存储基于更安全的 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;
}
}
}
规格
一个过滤器,可以通过 userDetailsService 调用 loadUserFromUsername(),以便从自定义 UserDetails 实例中检索租户数据库详细信息。
问题
无论过滤器优先级设置为什么,此自定义过滤器都会在安全过滤器之前运行,因此 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();
}
}
找到了一种非常有效的不同方式:Aspects!
所使用的切入点表达式意味着它会围绕该项目中控制器包中所有 类 的所有方法调用运行。
租户存储基于更安全的 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;
}
}
}