1. 程式人生 > >四十九、SpingBoot引入第三方filter

四十九、SpingBoot引入第三方filter

最近手頭活不多,被其他專案拉去幫忙。他們的需求是,專案中需要加入第三方的過濾器(用於單點登入認證的),專案採用的是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>

簡單粗暴而有效