四十九、SpingBoot引入第三方filter
阿新 • • 發佈:2018-12-28
最近手頭活不多,被其他專案拉去幫忙。他們的需求是,專案中需要加入第三方的過濾器(用於單點登入認證的),專案採用的是spring boot,spring boot之前沒深入玩過,所以費了些時間。
需求:
第三方提供了一個filter以及使用標準web.xml時的配置方法,要求整合到專案中
過程:
第一步:
作為不百度(google)不會寫程式碼的程式設計師,第一反應是去baidu一下,畢竟spring boot的使用停留在寫web api的碼磚水平上。百度結果,大部分都是用類似下面的方法:
@Bean public FilterRegistrationBean pluginFilter(){ //將第三方filter例項,配置資訊設定到一個FilterRegistrationBean中 //詳細程式碼省略,網上到處都是 }
優點在於:簡單。
缺點:這種第三方的filter,如果是多個的,每個都得重新加這種方法,對於擴充套件來說,囉嗦
於是,尋求spring boot專案直接使用web.xml裡過濾器配置的方案,百度了好些,都是直接將spring boot專案變為使用web.xml配置的,這個動作有點大,怕把專案給搞亂了,沒敢採用。。。
第二步:
目標明確為,使用Bean標籤返回FilterRegistrationBean(或類似的介面例項)的方向,但是這個Bean要能將多個過濾器註冊給spring。稍微讀了一下原始碼,發現FilterRegistrationBean這個鬼,這正起作用的是定義在AbstractFilterRegistrationBean中的方法onStartup方法
public void onStartup(ServletContext servletContext) throws ServletException { Filter filter = this.getFilter(); Assert.notNull(filter, "Filter must not be null"); String name = this.getOrDeduceName(filter); if (!this.isEnabled()) { this.logger.info("Filter " + name + " was not registered (disabled)"); } else { Dynamic added = servletContext.addFilter(name, filter);if (added == null) { this.logger.info("Filter " + name + " was not registered (possibly already registered?)"); } else { this.configure(added); } } }
所以,咱們重寫一下這個onStartup方法(類名字尾用的proxy可能不太合適,請不要在意細節):
package com.xxx.common.filter; import org.dom4j.Document; import org.dom4j.Element; import org.springframework.boot.web.servlet.FilterRegistrationBean; import javax.servlet.Filter; import javax.servlet.ServletContext; import javax.servlet.ServletException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class XxxFilterRegistrationBeanProxy extends FilterRegistrationBean{ private Document plugin_xml = null; //所有需要註冊的filter private Map<String,FilterRegistrationBean> filterRegistrationBeans; public IdssFilterRegistrationBeanProxy() { } public Document getPlugin_xml() { return plugin_xml; } public void setPlugin_xml(Document plugin_xml) { this.plugin_xml = plugin_xml; } public IdssFilterRegistrationBeanProxy(Document plugin_xml) throws IllegalAccessException, InstantiationException, ClassNotFoundException { super(); this.plugin_xml = plugin_xml; setFilterRegistrationBeans(); } @Override public void onStartup(ServletContext servletContext) throws ServletException { if(filterRegistrationBeans == null || filterRegistrationBeans.size()==0) return; //執行實際的FilterRegistrationBean的onStartup方法 for(FilterRegistrationBean bean : filterRegistrationBeans.values()){ bean.onStartup(servletContext); } } private void setFilterRegistrationBeans() throws ClassNotFoundException, IllegalAccessException, InstantiationException { //讀取xml配置檔案,例項化FilterRegistrationBean if(plugin_xml == null) return; filterRegistrationBeans = new HashMap<>(); Element root = plugin_xml.getRootElement(); Iterator<Element> filter_roots = root.elementIterator("filter"); int order = 0; while (filter_roots.hasNext()){ order++; Element filter_root = filter_roots.next(); String filter_name = filter_root.elementText("filter-name").trim(); String class_name = filter_root.elementText("filter-class").trim(); Filter filter = (Filter) Class.forName(class_name).newInstance(); FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(filter); filterRegistrationBean.setName(filter_name); filterRegistrationBean.setOrder(order); Iterator<Element> parm_settings = filter_root.elementIterator("init-param"); while (parm_settings.hasNext()){ Element parm = parm_settings.next(); filterRegistrationBean.addInitParameter(parm.elementText("param-name").trim(),parm.elementText("param-value").trim()); } filterRegistrationBeans.put(filter_name,filterRegistrationBean); } Iterator<Element> filter_mappings = root.elementIterator("filter-mapping"); while (filter_mappings.hasNext()){ Element filter_mapping = filter_mappings.next(); String filter_name = filter_mapping.elementText("filter-name"); if(filterRegistrationBeans.containsKey(filter_name)){ filterRegistrationBeans.get(filter_name).addUrlPatterns(filter_mapping.elementText("url-pattern")); } } } }
注入spring boot的單例類中,給出一個Bean註解的方法,返回上面類的例項
package com.xxx.common.filter; import com.xxx.common.utils.PropertyUtil; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.io.SAXReader; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.io.IOException; import java.io.InputStreamReader; @Component public class XxxPluginFilter { private XxxFilterRegistrationBeanProxy XxxFilterRegistrationBeanProxy = new XxxFilterRegistrationBeanProxy(); private Document plugin_xml = null; private final static String plugin_filter_path = PropertyUtil.getProperty("plugin_filter_path"); @Bean public FilterRegistrationBean pluginFilter(){ return XxxFilterRegistrationBeanProxy; } @PostConstruct private void init() throws IllegalAccessException, InstantiationException, ClassNotFoundException { if(plugin_filter_path == null || plugin_filter_path.isEmpty()) return; SAXReader reader = new SAXReader(); InputStreamReader inputStreamReader = null; try { inputStreamReader = new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream(plugin_filter_path)); plugin_xml = reader.read(inputStreamReader); xxxFilterRegistrationBeanProxy = new XxxFilterRegistrationBeanProxy(plugin_xml); } catch (DocumentException e) { e.printStackTrace(); } finally { if(inputStreamReader != null) { try { inputStreamReader.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
filter的xml配置檔案示例:
<?xml version="1.0" encoding="UTF-8"?> <plugin-filter> <filter> <filter-name>filter1</filter-name> <filter-class> com.test.Filter1 </filter-class> <init-param> <param-name>servername</param-name> <param-value>http://localhost:8081</param-value> </init-param> </filter> <filter> <filter-name>filter2</filter-name> <filter-class> com.test.Filter2 </filter-class> <init-param> <param-name>initteset</param-name> <param-value>yyyy</param-value> </init-param> </filter> <filter-mapping> <!-- 需要過濾的路徑和檔案型別 --> <filter-name>filter1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>filter2</filter-name> <url-pattern>/api/*</url-pattern> </filter-mapping> </plugin-filter>
簡單粗暴而有效