SpringBoot整合shiro系列-SpingBoot是如何将shiroFilter注册到servlet容器中的
Posted 剑气吾霜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot整合shiro系列-SpingBoot是如何将shiroFilter注册到servlet容器中的相关的知识,希望对你有一定的参考价值。
一、先从配置类入手,主要是@Bean了一个ShiroFilterFactoryBean:
@Data @Configuration @Slf4j @EnableConfigurationProperties(ShiroProperties.class) public class ShiroConfiguration { //.......这里省略了大部分跟主题无关的配置代码 /** * 这里名字叫 shiroFilter,后续通过FilterRegistrationBean从spring容器中向servlet容器中注册filter * @param securityManager * @return * @author * @create */ @Bean(name = "shiroFilter") public ShiroFilterFactoryBean getShiroFilterFactoryBean (DefaultWebSecurityManager securityManager, ShiroProperties sp,IUserService userService){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //配置shiroFilter子过滤器 loadFilter(shiroFilterFactoryBean,getCasFilter(sp,userService),getJwtFilter(),getWeComFilter()); //配置拦截规则 loadShiroFilterChain(shiroFilterFactoryBean, sp); return shiroFilterFactoryBean; } /** * CAS过滤器 */ @Bean public CustomizedCasFilter getCasFilter(ShiroProperties sp, IUserService userService) { CustomizedCasFilter casFilter = new CustomizedCasFilter(); //....... return casFilter; } /** * JWT过滤器 * * @return * @author * @create 2020年8月19日 */ @Bean public CustomizedJwtFilter getJwtFilter() { CustomizedJwtFilter jwtFilter = new CustomizedJwtFilter(); return jwtFilter; } /** * 企业微信过滤器 * @param * @return */ @Bean public CustomizedWeComFilter getWeComFilter() { CustomizedWeComFilter weComFilter = new CustomizedWeComFilter(); return weComFilter; } /** * 添加过滤器 * @param bean * @param casFilter * @param jwtFilter */ private void loadFilter(ShiroFilterFactoryBean bean,CasFilter casFilter, CustomizedJwtFilter jwtFilter, CustomizedWeComFilter weComFilter) { Map<String, Filter> filters = new HashMap<>(); filters.put("cas", casFilter); filters.put("jwt", jwtFilter); filters.put("weCom",weComFilter); bean.setFilters(filters); } /** * 加载shiroFilter权限控制规则 * * @author * @create */ private void loadShiroFilterChain (ShiroFilterFactoryBean shiroFilterFactoryBean, ShiroProperties sp){ //下面这些规则配置最好配置到配置文件中 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); // shiro集成cas后,首先添加该规则 filterChainDefinitionMap.put("/api/**", "jwt"); filterChainDefinitionMap.put("/cas/**", "cas"); filterChainDefinitionMap.put("/wecom/**", "weCom"); filterChainDefinitionMap.put("/test/**", "anon"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); } /** * 在spring容器处在某一声明周期时时,为shiro中的相关bean执行对应的bean生命周期方法 * * @author * @create */ @Bean(name = "lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor () { return new LifecycleBeanPostProcessor(); } ....... }
二、ShiroFilterFactoryBean(源码解析,代码中注释带序号的为代码执行顺序)-- 向Spring容器中注入了SpringShiroFilter:
public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor { private static final transient Logger log = LoggerFactory.getLogger(ShiroFilterFactoryBean.class); private SecurityManager securityManager; //这里是我们向注入的多个filter 见配置类中的loadFilter()方法 private Map<String, Filter> filters = new LinkedHashMap(); private List<String> globalFilters = new ArrayList(); //见配置类中loadShiroFilterChain private Map<String, String> filterChainDefinitionMap; private String loginUrl; private String successUrl; private String unauthorizedUrl; private AbstractShiroFilter instance; public ShiroFilterFactoryBean() { this.globalFilters.add(DefaultFilter.invalidRequest.name()); this.filterChainDefinitionMap = new LinkedHashMap(); } public SecurityManager getSecurityManager() { return this.securityManager; } ...... public Map<String, Filter> getFilters() { return this.filters; } public Map<String, String> getFilterChainDefinitionMap() { return this.filterChainDefinitionMap; } ...... // 1、 这里会在spring容器加载的时候调用,向spring容器中注入getObject()返回的对象 public Object getObject() throws Exception { if (this.instance == null) { //2、调用createInstance()方法 this.instance = this.createInstance(); } return this.instance; } public Class getObjectType() { return ShiroFilterFactoryBean.SpringShiroFilter.class; } public boolean isSingleton() { return true; } protected FilterChainManager createFilterChainManager() { DefaultFilterChainManager manager = new DefaultFilterChainManager(); Map<String, Filter> defaultFilters = manager.getFilters(); Iterator var3 = defaultFilters.values().iterator(); while(var3.hasNext()) { Filter filter = (Filter)var3.next(); this.applyGlobalPropertiesIfNecessary(filter); } //4、在这个地方获取了shiroFilter中的子过滤器,即我们在配置类中设置的loadFilter() Map<String, Filter> filters = this.getFilters(); String name; Filter filter; if (!CollectionUtils.isEmpty(filters)) { //5、这个地方迭代,并将子过滤器添加到了manager中 for(Iterator var10 = filters.entrySet().iterator(); var10.hasNext(); manager.addFilter(name, filter, false)) { Entry<String, Filter> entry = (Entry)var10.next(); name = (String)entry.getKey(); filter = (Filter)entry.getValue(); this.applyGlobalPropertiesIfNecessary(filter); if (filter instanceof Nameable) { ((Nameable)filter).setName(name); } } } manager.setGlobalFilters(this.globalFilters); Map<String, String> chains = this.getFilterChainDefinitionMap(); if (!CollectionUtils.isEmpty(chains)) { Iterator var12 = chains.entrySet().iterator(); while(var12.hasNext()) { Entry<String, String> entry = (Entry)var12.next(); String url = (String)entry.getKey(); String chainDefinition = (String)entry.getValue(); //这里很重要 后面DefaultFilterChainManager filterChains属性在这里添加值 manager.createChain(url, chainDefinition); } } //这里很重要 后面DefaultFilterChainManager filterChains属性在这里添加值 manager.createDefaultChain("/**"); return manager; } protected AbstractShiroFilter createInstance() throws Exception { log.debug("Creating Shiro Filter instance."); SecurityManager securityManager = this.getSecurityManager(); String msg; if (securityManager == null) { msg = "SecurityManager property must be set."; throw new BeanInitializationException(msg); } else if (!(securityManager instanceof WebSecurityManager)) { msg = "The security manager does not implement the WebSecurityManager interface."; throw new BeanInitializationException(msg); } else { //3、调用createFilterChainManager() FilterChainManager manager = this.createFilterChainManager(); PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver(); //6、将manager注入到chainResolver中,此时的manager有子过滤器,因而chainResolver也是可以获取到子过滤器的 chainResolver.setFilterChainManager(manager); //7、返回SpringShiroFilter对象,即向Spring容器中注入SpringShiroFilter bean对象(带chain) return new ShiroFilterFactoryBean.SpringShiroFilter((WebSecurityManager)securityManager, chainResolver); } } //.........删除无关代码 private void applyGlobalPropertiesIfNecessary(Filter filter) { this.applyLoginUrlIfNecessary(filter); this.applySuccessUrlIfNecessary(filter); this.applyUnauthorizedUrlIfNecessary(filter); } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof Filter) { log.debug("Found filter chain candidate filter \'{}\'", beanName); Filter filter = (Filter)bean; this.applyGlobalPropertiesIfNecessary(filter); this.getFilters().put(beanName, filter); } else { log.trace("Ignoring non-Filter bean \'{}\'", beanName); } return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } private static final class SpringShiroFilter extends AbstractShiroFilter { protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) { if (webSecurityManager == null) { throw new IllegalArgumentException("WebSecurityManager property cannot be null."); } else { this.setSecurityManager(webSecurityManager); if (resolver != null) { this.setFilterChainResolver(resolver); } } } } }
三、FilterRegistrationBean:上面两步已经将BeanName为shiroFilter的SpringShiroFilter注入到了Spring容器中,现在来说一下是如何从Spring容器中注册到Servlet容器中的。
一般我们在Spring中配置Filter是这样写的:
@Configuration public class FilterConfig { /** * 注册DelegatingFilterProxy(Shiro) * * @return * @author * @create 2020年8月19日 */ @Bean public FilterRegistrationBean shiroFilterRegistrationBean () { FilterRegistrationBean filterRegistration = new FilterRegistrationBean(); filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter")); // 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 filterRegistration.addInitParameter("targetFilterLifecycle", "true"); filterRegistration.setEnabled(true); filterRegistration.addUrlPatterns("/*"); return filterRegistration; } }
他具体的工作原理是什么?
1、Spring容器在启动时会调用org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext的selfInitialize(ServletContext servletContext)方法。
private void selfInitialize(ServletContext servletContext) throws ServletException { this.prepareWebApplicationContext(servletContext); this.registerApplicationScope(servletContext); WebApplicationContextUtils.registerEnvironmentBeans(this.getBeanFactory(), servletContext); Iterator var2 = this.getServletContextInitializerBeans().iterator(); while(var2.hasNext()) { ServletContextInitializer beans = (ServletContextInitializer)var2.next(); beans.onStartup(servletContext); } }
2、bean.onStartup(servletContext)调用 RegistrationBean.onStartup()方法,该方法又调用抽象方法register()。
public final void onStartup(ServletContext servletContext) throws ServletException { String description = this.getDescription(); if (!this.isEnabled()) { logger.info(StringUtils.capitalize(description) + " was not registered (disabled)"); } else { this.register(description, servletContext); } } protected abstract void register(String description, ServletContext servletContext);
3、register()方法由RegistrationBean的子类DynamicRegistrationBean实现,并调用由子类AbstractFilterRegistrationBean实现的addRegistration()方法。
protected final void register(String description, ServletContext servletContext) { D registration = this.addRegistration(description, servletContext); if (registration == null) { logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)"); } else { this.configure(registration); } } protected abstract D addRegistration(String description, ServletContext servletContext);
4、AbstractFilterRegistrationBean的addRegistration()方法,向ServletContext中注册了一个过滤器。而这个过滤器是子类提供的,也就是我们在Spring中注入的FilterRegistrationBean提供的,他的getFilter()方法
返回的就是我们的shiroFilter 。
AbstractFilterRegistrationBean:
protected Dynamic addRegistration(String description, ServletContext servletContext) { //调用子类实现的抽象方法 Filter filter = this.getFilter(); //向servletContext中注册Filter return servletContext.addFilter(this.getOrDeduceName(filter), filter); } //这个方法由子类实现 public abstract T getFilter();
FilterRegistrationBean:
public class FilterRegistrationBean<T extends Filter> extends AbstractFilterRegistrationBean<T> { private T filter; public FilterRegistrationBean() { super(new ServletRegistrationBean[0]); } public FilterRegistrationBean(T filter, ServletRegistrationBean<?>... servletRegistrationBeans) { super(servletRegistrationBeans); Assert.notNull(filter, "Filter must not be null"); this.filter = filter; } public T getFilter() { return this.filter; } public void setFilter(T filter) { Assert.notNull(filter, "Filter must not be null"); this.filter = filter; } }
至此,shiroFilter从Spring容器中注册到了Servlet容器中了。
以上是关于SpringBoot整合shiro系列-SpingBoot是如何将shiroFilter注册到servlet容器中的的主要内容,如果未能解决你的问题,请参考以下文章
全栈编程系列SpringBoot整合Shiro(含KickoutSessionControlFilter并发在线人数控制以及不生效问题配置启动异常No SecurityManager...)
SpringBoot整合shiro系列-SpingBoot是如何将shiroFilter注册到servlet容器中的
全栈编程系列SpringBoot整合Shiro(含KickoutSessionControlFilter并发在线人数控制以及不生效问题配置启动异常No SecurityManager...)(代码片段