首先了解下SpringBoot中三種Filter注冊方式:
- 直接使用@WebFilter 注解
@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("-----doFilter-----");
chain.doFilter(request, response);
}
}
- 聲明為Bean
@Bean
public MyFilter myFilter() {
return new MyFilter();
}
- 定義FilterRegistrationBean
@Bean
FilterRegistrationBean<MyFilter> myFilterRegistrationBean() {
FilterRegistrationBean<MyFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new MyFilter());
bean.setOrder(-1);
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
以上三種聲明后,都會在ServletContextInitializerBeans
中進行初始化。
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
//初始化ServletContextInitializer類型的Bean,例如:FilterRegistrationBean
addServletContextInitializerBeans(beanFactory);
//初始化直接聲明為Bean的Filter,如果上一步已經識別到了,該步驟就會忽略掉該Filter。
addAdaptableBeans(beanFactory);
List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}
Shiro中自定義Filter
Shiro自定義Filter后需要注冊到ShiroFilterFactoryBean 中。如果直接通過@Bean聲明后再加入,這個Filter將會被Spring接管,然后自動注冊到ApplicationFilter,導致被重復執行。
有兩種方式避免:
- 注冊到ShiroFilterFactoryBean直接new出來,這樣Spring不會接管該filter,
filters.put("myFilter", new MyShiroFilter());
- 使用@Bean聲明后,在通過FilterRegistrationBean設置不加載到ApplicationFilter
@Bean
public MyShiroFilter myShiroFilter() {
return new MyShiroFilter();
}
@Bean
public FilterRegistrationBean<MyShiroFilter> captchaAuthcRegistrationBean(MyShiroFilter filter) {
FilterRegistrationBean<MyShiroFilter> registration = new FilterRegistrationBean<>(filter);
//設置為不注冊為ApplicationFilter
registration.setEnabled(false);
return registration;
}
如何自動識別Shiro自定義Filter,不自動注冊ApplicationFilter
有時候寫通用模塊的時候,需要自動發現Shiro自定義Filter,并自動注冊。自動發現可以通過聲明為Bean。
但是聲明為Bean之后就會被注冊為ApplicationFilter
。難道每次都要寫個FilterRegistrationBean
來防止注冊為ApplicationFilter
?
可以通過BeanFactoryPostProcessor
來擴展實現,也就是找到Shiro自定義Filter,然后自動注冊一個FilterRegistrationBean
。
實現步驟:
- 定義一個
CustomShiroFilter
,所有自定義的Filter實現該接口
public interface CustomShiroFilter extends Filter {
}
- 定義一個
ShiroFilterRegistrationBeanPostProcessor
組件
public class ShiroFilterRegistrationBeanPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
//找到所有自定義的ShiroFilter,并注冊FilterRegistrationBean
String[] beanNames = beanFactory.getBeanNamesForType(CustomShiroFilter.class);
for (String beanName : beanNames) {
registerFilterRegistrationBean(defaultListableBeanFactory, beanName);
}
}
private static void registerFilterRegistrationBean(DefaultListableBeanFactory beanFactory, String beanName) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(FilterRegistrationBean.class);
beanDefinitionBuilder.addPropertyReference("filter", beanName);
beanDefinitionBuilder.addPropertyValue("enabled", false);
beanFactory.registerBeanDefinition(beanName + "FilterRegistrationBean",
beanDefinitionBuilder.getRawBeanDefinition());
}
}
- 引入該組件
@Configuration
@Import({ShiroFilterRegistrationBeanPostProcessor.class})
public class ShiroAutoConfiguration {
}
使用:后面可以隨便的自定義Filter并直接聲明為Bean了,不需要再去設置false了。
自定義一個Shiro過濾器繼承CustomShiroFilter
public class MyShiroFilter extends AuthorizationFilter implements CustomShiroFilter {
...
}