在NutzBoot中拆分Shiro.ini配置文件使 Urls 能夠配置多個並和 main 配置分離

Nutzboot簡介:html

 NutzBoot爲一個基於Nutz框架的微服務方案,如下簡稱NB!其他略...java

 NB文檔簡介:http://www.nutzam.com/core/bo...web

簡介:apache

 首先關於Nutz以及NB,shiro等介紹以及微服務的概念在此略過,本文中介紹的方法基於Nutz框架,但實現思路和核心類應該能夠在其餘框架中通用.服務器

一.首先闡述一下研究這個東西的出發點.

 一開始在NB中也是使用的shiro.ini配置文件完成對shiro的配置的,可是在實際開發過程當中發現,在配置微服務的過程當中,shiro文件須要配置或複製屢次,這裏插一嘴咱們的實現方式,咱們服務的實現方式爲,全部須要獨立部署的服務器都會實現同一個基礎服務器運行庫,來完成通用配置等.app

 在配置多個服務器的時候發現不一樣服務器之間若是隻使用一個shiro.ini配置文件會出現,[urls]配置下的url重複的狀況,即例如先後臺的/user/login接口,既是在NB的配置中配置不一樣服務器上下文,也沒法避免同一個shiro配置文件出現此問題框架

 爲了解決這個問題其實也想過將不一樣服務的配置文件分離(固然這在NB結構中是能夠實現的),可是出現shiro.ini中[main]等配置被複制屢次的狀況,不美觀也不利於修改.ide

二.解決思路

 爲了解決這個問題其實一開始想到的是經過NB中shiro-stater中已有的 shiro.ini.urls 配置解決(這個配置容許在其餘一個文件中手寫shiro urls 配置列表)微服務

 後來發現這個配置沒法和 shiro.ini配置文件一塊兒使用(這個配置大概是給使用默認配置的同窗使用的)優化

 而已有的shiro大部分配置都在shiro.ini配置文件中,不太可能寫成代碼形式,只能另尋他法

 最後通過幾經尋找,最終肯定能解決這個問題的方法是(也修改了不少次,這裏說了最後兩種)

 1.複製shiro-starter源碼,利用其中已有的重寫shiro的 EnvironmentLoaderListener的類,以和以前部分相同的邏輯初始化Shiro.

 2.修改上述Listener在傳入Environment處作修改(此處省略了starter獲取配置文件路徑並傳入的部分,以及配置非空部分),傳入一個重寫的Environment,這個重寫的類中大部分實現邏輯是由 shiro處理源碼IniWebEnvironment類複製過來的,用來完成 shiro.ini文件的配置加載.

 3.修改重寫類代碼,使其能夠獲取到NB starter的配置(略,用來傳入路徑或urls),並修改源碼中獲取Filter的方法createFilterChainResolver()

 在其建立Filter實例以前插入代碼

ini.load(String);

 源碼:(此處檢查了是否有 urls和filter配置)

// only create a resolver if the 'filters' or 'urls' sections are defined:
    Ini.Section urls = ini.getSection(IniFilterChainResolverFactory.URLS);
    Ini.Section filters = ini.getSection(IniFilterChainResolverFactory.FILTERS);
    if (!CollectionUtils.isEmpty(urls) || !CollectionUtils.isEmpty(filters)) {
        // either the urls section or the filters section was defined. Go ahead and create the resolver:
        IniFilterChainResolverFactory factory = new IniFilterChainResolverFactory(ini, this.objects);
        resolver = factory.getInstance();
    }

 修改後的代碼(此處須要注意咱們邏輯中沒有 filter配置,這裏就沒判斷)

//urls爲符合 ini語法的字符串 例如 /user/login = anon
    String iniUrls = "[urls]\r\n" + urls;
    ini.load(iniUrls);
    // 此代碼前,在此處ini會被加載成類
    // ini對象在此方法以前就存在,this.object則由父類中負責
    IniFilterChainResolverFactory factory = new IniFilterChainResolverFactory(ini, this.objects);
    resolver = factory.getInstance();

 這裏須要注意在調用 ini.load後原文件中的 [urls] 配置會被覆蓋,而當只有[urls] 但並無實際內容時並不會被覆蓋(沒試過空行,請注意)

 4.在完成這段代碼後發現代碼很是難看,shiro urls被配置到了java文件中,爲了不這個問題使用了NB-start直接帶的配置方式以及已經存在的 shiro.ini.urls屬性,將配置文件中的 urls傳入代碼中

 因此在NB中就成了這樣

//獲取配置文件中urls
    String urls = conf.get(ShiroEnvStarter.PROP_INIT_URLS, null);
    //urls爲符合 ini語法的字符串 例如 /user/login = anon
    String iniUrls = "[urls]\r\n" + urls;
    ini.load(iniUrls);
    // 此代碼前,在此處ini會被加載成類
    // ini對象在此方法以前就存在,this.object則由父類中負責
    IniFilterChainResolverFactory factory = new IniFilterChainResolverFactory(ini, this.objects);
    resolver = factory.getInstance();

 在NB的application.properties配置文件中寫法爲

shiro.ini.urls:
    /unathenticated = anon
    /user/login = anon
    #end

 這裏還要手動@並感謝下 @wendal @nutzcn
 以前並不知道application.properties中多行的寫法

 至此,整個在代碼中覆蓋 shiro urls 配置的思路基本就完成了,只剩下一些相似於非空判斷參數獲取的代碼.在此實現下便可實現項目中Shiro main配置爲統一文件,而urls配置爲專用單獨文件

 以後要介紹的就是優化部分以及,完整代碼

三.優化(徹底基於NB)

 1.在使用中發現雖然如今 urls已經能夠獨立文件配置,但多行之間並不能夠寫註解(在NB層就不會讀取完整),而且和 shiro.ini中語法並不徹底同樣.

 最後解決辦法爲:在項目中添加另外一個.ini的配置文件,並在項目中讀取ini文件並使用 ini.load加載

 2.在添加了ini配置文件後發現最好能夠有一部分urls能夠通用配置,另外一部分能夠獨立配置.

 最後解決方式爲:添加兩個shiro-url.ini配置文件,並在代碼中讀取並拼接,在庫服務中添加,一個ini文件,並在每一個獨立的服務中添加徹底同路徑和名稱的文件,使用其打包時互相覆蓋的特性,達到獨立配置的目的,而另外一個不進行覆蓋達到通用配置的目的.

 而在NB中經過在 starter源碼中添加新的配置達到url能夠配置並傳入的目的.

@PropDoc(value = "urls過濾清單ini文件1路徑")
    public static final String PROP_INIT_URLS_PATH1 = "shiro.ini.urls.path1";

    @PropDoc(value = "urls過濾清單ini文件2路徑")
    public static final String PROP_INIT_URLS_PATH2 = "shiro.ini.urls.path2";

 這裏附上讀取並拼接兩個 ini的代碼

private boolean loadIniPath1ANd2(Ini ini) {
        String urlsAll = "";
        String urls1 = readConfig(ShiroEnvStarter.PROP_INIT_URLS_PATH1);
        String urls2 = readConfig(ShiroEnvStarter.PROP_INIT_URLS_PATH2);

        urlsAll = urls1 + urls2;
        if (!urlsAll.equals("")) {
            String iniUrls = "[urls]\r\n" + urlsAll;
            log.info("shiro ini urls ---> \r\n" + iniUrls);
            // Ini ini = new Ini();
            // ini.load(iniUrls);
            // Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
            // Log.info(section.toString());
            
            // 注意這個代碼中這裏load過一次了
            ini.load(iniUrls);

            // 此處驗證了 ini並無全被覆蓋,只覆蓋了 加載的配置文件部分
            // 若是新加載的內容中 只有 [main] 標籤不會被覆蓋,可是若是 [main]標籤下有內容 則會覆蓋以前的配置
            // for (Entry<String, Ini.Section> entry : ini.entrySet()) {
            // jline.internal.Log.info(entry.getKey());
            // for (Entry<String, String> entryStr : entry.getValue().entrySet()) {
            // jline.internal.Log.info(entryStr);
            // }
            // }

            return true;
        }

        return false;
    }
    
    private String readConfig(String confPath) {
        String urls1 = "";
        try {
            String path = conf.get(confPath, "").trim();
            log.info("path:" + path);
            if (path != null && appContext.getResourceLoader().has(path)) {
                InputStream is = ResourceUtils.getInputStreamForPath("classpath:" + path);
                if (is != null) {
                    urls1 = readIniFile(is);
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return urls1;
    }

    private String readIniFile(InputStream is) throws IOException {
        // InputStreamReader reader =new InputStreamReader(new
        // FileInputStream(file),"UTF-8");
        InputStreamReader reader = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(reader);

        StringBuffer sbf = new StringBuffer();
        while (true) {
            String str = br.readLine();
            if (str != null) {
                sbf.append(str).append("\r\n");
            } else {
                break;
            }
        }
        br.close();
        reader.close();

        return sbf.toString();
    }

 固然代碼中依舊有能夠優化的地方,不過這裏就先不寫了,嘿嘿

 不過至此已經達到能想到的最好的效果了,父子項目中 父項目配置 shiro main中驗證和DB,Redis的大部分通用配置,以及全部通用的urls過濾,而子類能夠添加新的urls配置,而且各個子項目中 shiro配置並不衝突.

 至此整偏博客結束,若是其中有錯誤也請你們告訴我了~~

 下面是源碼部分,不看能夠跳過了!~

四.源碼部分

 starter源碼部分

 不包括starter中未修改的類

 ShiroEnvStarter.java

//添加常量 
@PropDoc(value = "urls過濾清單ini文件1路徑")
public static final String PROP_INIT_URLS_PATH1 = "shiro.ini.urls.path1";

@PropDoc(value = "urls過濾清單ini文件2路徑")
public static final String PROP_INIT_URLS_PATH2 = "shiro.ini.urls.path2";

 NbShiroEnvironmentLoaderListener.java

//修改

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;

import org.apache.shiro.ShiroException;
import org.apache.shiro.config.ConfigurationException;
import org.apache.shiro.util.ClassUtils;
import org.apache.shiro.util.UnknownClassException;
import org.apache.shiro.web.env.EnvironmentLoader;
import org.apache.shiro.web.env.EnvironmentLoaderListener;
import org.apache.shiro.web.env.IniWebEnvironment;
import org.nutz.boot.AppContext;
import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.lang.Lang;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jline.internal.Log;

public class NbShiroEnvironmentLoaderListener extends EnvironmentLoaderListener {

    private static final Logger log = LoggerFactory.getLogger(NbShiroEnvironmentLoaderListener.class);

    protected PropertiesProxy conf;

    protected AppContext appContext;

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        PropertiesProxy conf = appContext.getConfigureLoader().get();

        try {
            boolean hasUrlsPath = hasIniUrlsPath(conf, appContext);
            // 走原生API的shiro.ini文件嗎?
            String iniPath = conf.get("shiro.ini.path", "shiro.ini");
            boolean hasIniPath = conf.has("shiro.ini.path") || appContext.getResourceLoader().has(iniPath);
            if (hasIniPath || (hasIniPath && hasUrlsPath)) {
                sce.getServletContext().setAttribute(EnvironmentLoader.CONFIG_LOCATIONS_PARAM, iniPath);
                super.contextInitialized(sce);
                return;
            }
        } catch (Exception e) {
            throw Lang.wrapThrow(e);
        }
        // 沒有配置文件 走nb(默認)配置
        sce.getServletContext().setAttribute(ENVIRONMENT_CLASS_PARAM, NbResourceBasedWebEnvironment.class.getName());
        super.contextInitialized(sce);
    }

    protected Class<?> determineWebEnvironmentClass(ServletContext servletContext) {
        // nb 默認配置
        String className = servletContext.getInitParameter(ENVIRONMENT_CLASS_PARAM);
        if (className != null) {
            try {
                return ClassUtils.forName(className);
            } catch (UnknownClassException ex) {
                throw new ConfigurationException("Failed to load custom WebEnvironment class [" + className + "]", ex);
            }
        } else {
            try {
                boolean hasUrlsPath = hasIniUrlsPath(conf, appContext);
                // 若是有配置文件
                String iniPath = conf.get("shiro.ini.path", "shiro.ini");
                boolean hasIniPath = conf.has("shiro.ini.path") || appContext.getResourceLoader().has(iniPath);
                if (hasIniPath && hasUrlsPath) {
                    return IniMixWebEnvironment.class;
                } else if (hasIniPath) {
                    return IniWebEnvironment.class;
                }
            } catch (IOException e) {
                throw new ShiroException(e);
            }
            return NbResourceBasedWebEnvironment.class;
        }
    }

    private boolean hasIniUrlsPath(PropertiesProxy conf, AppContext appContext) throws IOException {
        String iniUrlsPath1 = conf.get(ShiroEnvStarter.PROP_INIT_URLS_PATH1, null);
        boolean hasIniUrlsPath1 = false;
        if (iniUrlsPath1 != null) {
            hasIniUrlsPath1 = (conf.has(ShiroEnvStarter.PROP_INIT_URLS_PATH1)
                    || appContext.getResourceLoader().has(iniUrlsPath1));
        }

        String iniUrlsPath2 = conf.get(ShiroEnvStarter.PROP_INIT_URLS_PATH2, null);
        boolean hasIniUrlsPath2 = false;
        if (iniUrlsPath2 != null) {
            hasIniUrlsPath2 = (conf.has(ShiroEnvStarter.PROP_INIT_URLS_PATH2)
                    || appContext.getResourceLoader().has(iniUrlsPath2));
        }

        if (hasIniUrlsPath1 || hasIniUrlsPath2) {
            return true;
        }

        return false;
    }
}

基於 shiro原生實現的實現類

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Map;

import javax.servlet.ServletContext;

import org.apache.shiro.config.ConfigurationException;
import org.apache.shiro.config.Ini;
import org.apache.shiro.config.IniFactorySupport;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.util.Destroyable;
import org.apache.shiro.util.Initializable;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.config.IniFilterChainResolverFactory;
import org.apache.shiro.web.config.WebIniSecurityManagerFactory;
import org.apache.shiro.web.env.IniWebEnvironment;
import org.apache.shiro.web.env.ResourceBasedWebEnvironment;
import org.apache.shiro.web.env.WebEnvironment;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.util.WebUtils;
import org.nutz.boot.AppContext;
import org.nutz.ioc.Ioc;
import org.nutz.ioc.impl.PropertiesProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * {@link WebEnvironment} implementation configured by an {@link Ini} instance
 * or {@code Ini} resource locations.
 *
 * 代碼主要實現部分由shiro IniWebEnvironment源碼抄過來的 <br>
 * 修改了 createFilterChainResolver 方法,讓 ini又load了一個 urls 配置 <br>
 * 目前從現象看 ini.load 彷佛不會覆蓋整個配置
 *
 * @since 1.2
 */
public class IniMixWebEnvironment extends ResourceBasedWebEnvironment implements Initializable, Destroyable {

    // *********非源碼部分**********
    protected AppContext appContext;
    protected Ioc ioc;
    protected PropertiesProxy conf;
    // ****************************

    public static final String DEFAULT_WEB_INI_RESOURCE_PATH = "/WEB-INF/shiro.ini";

    private static final Logger log = LoggerFactory.getLogger(IniWebEnvironment.class);

    /**
     * The Ini that configures this WebEnvironment instance.
     */
    private Ini ini;

    /**
     * Initializes this instance by resolving any potential (explicit or
     * resource-configured) {@link Ini} configuration and calling
     * {@link #configure() configure} for actual instance configuration.
     */
    public void init() {
        // *********非源碼部分**********
        appContext = AppContext.getDefault();
        ioc = appContext.getIoc();
        conf = appContext.getConfigureLoader().get();
        // ****************************

        Ini ini = getIni();

        String[] configLocations = getConfigLocations();

        if (log.isWarnEnabled() && !CollectionUtils.isEmpty(ini) && configLocations != null
                && configLocations.length > 0) {
            log.warn("Explicit INI instance has been provided, but configuration locations have also been "
                    + "specified.  The {} implementation does not currently support multiple Ini config, but this may "
                    + "be supported in the future. Only the INI instance will be used for configuration.",
                    IniWebEnvironment.class.getName());
        }

        if (CollectionUtils.isEmpty(ini)) {
            log.debug("Checking any specified config locations.");
            ini = getSpecifiedIni(configLocations);
        }

        if (CollectionUtils.isEmpty(ini)) {
            log.debug("No INI instance or config locations specified.  Trying default config locations.");
            ini = getDefaultIni();
        }

        if (CollectionUtils.isEmpty(ini)) {
            String msg = "Shiro INI configuration was either not found or discovered to be empty/unconfigured.";
            throw new ConfigurationException(msg);
        }

        setIni(ini);

        configure();
    }

    protected void configure() {

        this.objects.clear();

        WebSecurityManager securityManager = createWebSecurityManager();
        setWebSecurityManager(securityManager);

        FilterChainResolver resolver = createFilterChainResolver();
        if (resolver != null) {
            setFilterChainResolver(resolver);
        }
    }

    protected Ini getSpecifiedIni(String[] configLocations) throws ConfigurationException {

        Ini ini = null;

        if (configLocations != null && configLocations.length > 0) {

            if (configLocations.length > 1) {
                log.warn(
                        "More than one Shiro .ini config location has been specified.  Only the first will be "
                                + "used for configuration as the {} implementation does not currently support multiple "
                                + "files.  This may be supported in the future however.",
                        IniWebEnvironment.class.getName());
            }

            // required, as it is user specified:
            ini = createIni(configLocations[0], true);
        }

        return ini;
    }

    protected Ini getDefaultIni() {

        Ini ini = null;

        String[] configLocations = getDefaultConfigLocations();
        if (configLocations != null) {
            for (String location : configLocations) {
                ini = createIni(location, false);
                if (!CollectionUtils.isEmpty(ini)) {
                    log.debug("Discovered non-empty INI configuration at location '{}'.  Using for configuration.",
                            location);
                    break;
                }
            }
        }

        return ini;
    }

    /**
     * Creates an {@link Ini} instance reflecting the specified path, or
     * {@code null} if the path does not exist and is not required.
     * <p/>
     * If the path is required and does not exist or is empty, a
     * {@link ConfigurationException} will be thrown.
     *
     * @param configLocation the resource path to load into an {@code Ini} instance.
     * @param required if the path must exist and be converted to a non-empty
     *            {@link Ini} instance.
     * @return an {@link Ini} instance reflecting the specified path, or
     *         {@code null} if the path does not exist and is not required.
     * @throws ConfigurationException if the path is required but results in a null
     *             or empty Ini instance.
     */
    protected Ini createIni(String configLocation, boolean required) throws ConfigurationException {

        Ini ini = null;

        if (configLocation != null) {
            ini = convertPathToIni(configLocation, required);
        }
        if (required && CollectionUtils.isEmpty(ini)) {
            String msg = "Required configuration location '" + configLocation + "' does not exist or did not "
                    + "contain any INI configuration.";
            throw new ConfigurationException(msg);
        }

        return ini;
    }

    protected FilterChainResolver createFilterChainResolver() {
        FilterChainResolver resolver = null;

        Ini ini = getIni();

        if (!CollectionUtils.isEmpty(ini)) {
            // only create a resolver if the 'filters' or 'urls' sections are defined:
            // Ini.Section urls = ini.getSection(IniFilterChainResolverFactory.URLS);
            // Ini.Section filters = ini.getSection(IniFilterChainResolverFactory.FILTERS);
            // *********非源碼部分**********
            // boolean loadUrls = loadIniUrls(ini);
            boolean loadUrls = loadIniPath1ANd2(ini);
            if (loadUrls) {
                // ****************************
                // if (!CollectionUtils.isEmpty(urls) || !CollectionUtils.isEmpty(filters)) {
                // either the urls section or the filters section was defined. Go ahead and create the resolver:
                IniFilterChainResolverFactory factory = new IniFilterChainResolverFactory(ini, this.objects);
                resolver = factory.getInstance();
            }
        }

        return resolver;
    }

    // *********非源碼部分**********
    private boolean loadIniPath1ANd2(Ini ini) {
        String urlsAll = "";
        String urls1 = readConfig(ShiroEnvStarter.PROP_INIT_URLS_PATH1);
        String urls2 = readConfig(ShiroEnvStarter.PROP_INIT_URLS_PATH2);

        urlsAll = urls1 + urls2;
        if (!urlsAll.equals("")) {
            String iniUrls = "[urls]\r\n" + urlsAll;
            log.info("shiro ini urls ---> \r\n" + iniUrls);
            // Ini ini = new Ini();
            // ini.load(iniUrls);
            // Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
            // Log.info(section.toString());
            ini.load(iniUrls);

            // 此處驗證了 ini並無全被覆蓋,只覆蓋了 加載的配置文件部分
            // 若是新加載的內容中 只有 [main] 標籤不會被覆蓋,可是若是 [main]標籤下有內容 則會覆蓋以前的配置
            // for (Entry<String, Ini.Section> entry : ini.entrySet()) {
            // jline.internal.Log.info(entry.getKey());
            // for (Entry<String, String> entryStr : entry.getValue().entrySet()) {
            // jline.internal.Log.info(entryStr);
            // }
            // }

            return true;
        }

        return false;
    }

    private String readConfig(String confPath) {
        String urls1 = "";
        try {
            String path = conf.get(confPath, "").trim();
            log.info("path:" + path);
            if (path != null && appContext.getResourceLoader().has(path)) {
                InputStream is = ResourceUtils.getInputStreamForPath("classpath:" + path);
                if (is != null) {
                    urls1 = readIniFile(is);
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return urls1;
    }

    private String readIniFile(InputStream is) throws IOException {
        // InputStreamReader reader =new InputStreamReader(new
        // FileInputStream(file),"UTF-8");
        InputStreamReader reader = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(reader);

        StringBuffer sbf = new StringBuffer();
        while (true) {
            String str = br.readLine();
            if (str != null) {
                sbf.append(str).append("\r\n");
            } else {
                break;
            }
        }
        br.close();
        reader.close();

        return sbf.toString();
    }

    private boolean loadIniUrls(Ini ini) {
        // 使用了 原 start中定義的 shiro.ini.urls
        String config = conf.get(ShiroEnvStarter.PROP_INIT_URLS, "").trim();
        if (config != null && !config.equals("")) {
            String iniUrls = "[urls]\r\n" + config;
            log.info("shiro ini urls ---> \r\n" + iniUrls);
            // Ini ini = new Ini();
            // ini.load(iniUrls);
            // Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
            // Log.info(section.toString());
            ini.load(iniUrls);
            return true;
        }
        return false;
    }
    // ****************************

    protected WebSecurityManager createWebSecurityManager() {
        WebIniSecurityManagerFactory factory;
        Ini ini = getIni();
        if (CollectionUtils.isEmpty(ini)) {
            factory = new WebIniSecurityManagerFactory();
        } else {
            factory = new WebIniSecurityManagerFactory(ini);
        }

        WebSecurityManager wsm = (WebSecurityManager) factory.getInstance();

        // SHIRO-306 - get beans after they've been created (the call was before the
        // factory.getInstance() call,
        // which always returned null.
        Map<String, ?> beans = factory.getBeans();
        if (!CollectionUtils.isEmpty(beans)) {
            this.objects.putAll(beans);
        }

        return wsm;
    }

    /**
     * Returns an array with two elements, {@code /WEB-INF/shiro.ini} and
     * {@code classpath:shiro.ini}.
     *
     * @return an array with two elements, {@code /WEB-INF/shiro.ini} and
     *         {@code classpath:shiro.ini}.
     */
    protected String[] getDefaultConfigLocations() {
        return new String[] { DEFAULT_WEB_INI_RESOURCE_PATH, IniFactorySupport.DEFAULT_INI_RESOURCE_PATH };
    }

    /**
     * Converts the specified file path to an {@link Ini} instance.
     * <p/>
     * If the path does not have a resource prefix as defined by
     * {@link org.apache.shiro.io.ResourceUtils#hasResourcePrefix(String)}, the path
     * is expected to be resolvable by the {@code ServletContext} via
     * {@link javax.servlet.ServletContext#getResourceAsStream(String)}.
     *
     * @param path the path of the INI resource to load into an INI instance.
     * @param required if the specified path must exist
     * @return an INI instance populated based on the given INI resource path.
     */
    private Ini convertPathToIni(String path, boolean required) {

        // TODO - this logic is ugly - it'd be ideal if we had a Resource API to
        // polymorphically encaspulate this behavior

        Ini ini = null;

        if (StringUtils.hasText(path)) {
            InputStream is = null;

            // SHIRO-178: Check for servlet context resource and not only resource paths:
            if (!ResourceUtils.hasResourcePrefix(path)) {
                is = getServletContextResourceStream(path);
            } else {
                try {
                    is = ResourceUtils.getInputStreamForPath(path);
                } catch (IOException e) {
                    if (required) {
                        throw new ConfigurationException(e);
                    } else {
                        if (log.isDebugEnabled()) {
                            log.debug("Unable to load optional path '" + path + "'.", e);
                        }
                    }
                }
            }
            if (is != null) {
                ini = new Ini();
                ini.load(is);
            } else {
                if (required) {
                    throw new ConfigurationException("Unable to load resource path '" + path + "'");
                }
            }
        }

        return ini;
    }

    // TODO - this logic is ugly - it'd be ideal if we had a Resource API to
    // polymorphically encaspulate this behavior
    private InputStream getServletContextResourceStream(String path) {
        InputStream is = null;

        path = WebUtils.normalize(path);
        ServletContext sc = getServletContext();
        if (sc != null) {
            is = sc.getResourceAsStream(path);
        }

        return is;
    }

    /**
     * Returns the {@code Ini} instance reflecting this WebEnvironment's
     * configuration.
     *
     * @return the {@code Ini} instance reflecting this WebEnvironment's
     *         configuration.
     */
    public Ini getIni() {
        return this.ini;
    }

    /**
     * Allows for configuration via a direct {@link Ini} instance instead of via
     * {@link #getConfigLocations() config locations}.
     * <p/>
     * If the specified instance is null or empty, the fallback/default
     * resource-based configuration will be used.
     *
     * @param ini the ini instance to use for creation.
     */
    public void setIni(Ini ini) {
        this.ini = ini;
    }
}

若是之後想到什麼再補充吧

相關文章
相關標籤/搜索