zhuyumu 发表于 2018-11-19 12:15:47

Apache Shiro学习笔记(七)IniWebEnvironment

鲁春利的工作笔记,好记性不如烂笔头
  

  IniWebEnvironment
  在通过JavaSE方式解析权限信息时,是通过IniSecurityManagerFactory来实现的,其中也指定了默认的ini文件路径(IniFactorySupport):
public static final String DEFAULT_INI_RESOURCE_PATH = "classpath:shiro.ini";  

  IniWebEnvironment提供了在JavaWeb下加载解析权限信息的方式。

package org.apache.shiro.web.env;
/**
* 通过ini配置文件或资源路径实现WebEnvironment配置功能
*/
public class IniWebEnvironment extends ResourceBasedWebEnvironment implements Initializable, Destroyable {
    public static final String DEFAULT_WEB_INI_RESOURCE_PATH = "/WEB-INF/shiro.ini";
    private Ini ini;
    public void init() {
      // ini未被设置过,getIni获取到null
      Ini ini = getIni();
      // 在EnvironmentLoaderListener分析时,通过ResourceBasedWebEnvironment.setConfigLocations设置了config文件路径
      String[] configLocations = getConfigLocations();
      if (log.isWarnEnabled() && !CollectionUtils.isEmpty(ini) && configLocations != null && configLocations.length > 0) {
            // 告警信息:ini已经被配置了,但config也被配置了,暂时IniWebEnvironment不支持同时配置,不过将来可能会支持
      }
      // 通过configLocations路径对应的ini文件创建Ini实例
      if (CollectionUtils.isEmpty(ini)) {
            log.debug("Checking any specified config locations.");
            ini = getSpecifiedIni(configLocations);
      }
      // 如果通过configLocations路径配置的ini文件创建Ini实例失败
      if (CollectionUtils.isEmpty(ini)) {
            log.debug("No INI instance or config locations specified.Trying default config locations.");
            ini = getDefaultIni();    // 获取默认的Ini
      }
      if (CollectionUtils.isEmpty(ini)) {
            // 实例化ini对象失败,可能是configLocations路径配置错误或配置的文件无效
      }
      // 到了这里再进行set,设置ini实例
      setIni(ini);
      // 真正的权限解析及过滤的代码实现
      configure();
    }
    protected void configure() {
      // this.objects为父类DefaultEnvironment的变量protected final Map objects;
      this.objects.clear();
      WebSecurityManager securityManager = createWebSecurityManager();
      // 父类DefaultWebEnvironment的setWebSecurityManager方法,会继续调用其父类DefaultEnvironment
      setWebSecurityManager(securityManager);
      // ShiroFilter中setSecurityManager(env.getWebSecurityManager());也清晰了
      // 实际得到的是PathMatchingFilterChainResolver
      FilterChainResolver resolver = createFilterChainResolver();
      if (resolver != null) {    // 调用DefaultWebEnvironment.setObject方法进行设置,在ShiroFilter中就可以获取到了
            setFilterChainResolver(resolver);
      }
    }
    // 创建SecurityManager(DefaultWebSecurityManager)
    protected WebSecurityManager createWebSecurityManager() {
      // public class WebIniSecurityManagerFactory extends IniSecurityManagerFactory
      // 和在JavaSE环境下使用IniSecurityManagerFactory的方式一样了
      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 beans = factory.getBeans();
      if (!CollectionUtils.isEmpty(beans)) {
            this.objects.putAll(beans);
      }
      return wsm;
    }
    // 根据configLocations配置的ini文件生成ini实例(预处理configLocations)
    protected Ini getSpecifiedIni(String[] configLocations) throws ConfigurationException {
      Ini ini = null;
      if (configLocations != null && configLocations.length > 0) {
            if (configLocations.length > 1) {
                // 告警信息:配置了多个shiro.ini文件,但是只有第一个会被使用,现在还不支持配置多个,将来可能支持
            }
            //required = true, as it is user specified : 创建失败抛出ConfigurationException异常
            ini = createIni(configLocations, true);
      }
      return ini;
    }
    // 根据configLocations配置的ini文件生成ini实例(真正生成ini的操作)
    protected Ini createIni(String configLocation, boolean required) throws ConfigurationException {
      Ini ini = null;
      if (configLocation != null) {
            // convertPathToIni中以流的形式加载configLocation文件(ResourceUtils.getInputStreamForPath(path))
            // configLocation在web.xml的配置支持classpath:、url:、file:等多种形式。
            // ini = new Ini(); ini.load(is);    // 在ini的load方法中会解析ini文件(如、、)并进行封装
            ini = convertPathToIni(configLocation, required);
      }
      if (required && CollectionUtils.isEmpty(ini)) {
            // 抛出异常,ini文件没有找到无法创建ini实例
      }
      return ini;
    }
    // 如果通过指定的configLocations无法创建Ini实例,则使用默认加载路径尝试生成Ini实例
    protected Ini getDefaultIni() {
      Ini ini = null;
      // 获取系统默认ini文件路径
      String[] configLocations = getDefaultConfigLocations();
      if (configLocations != null) {
            for (String location : configLocations) {
                // required = false,系统默认ini文件创建Ini实例,创建失败日志会记录但不会抛异常
                ini = createIni(location, false);
                if (!CollectionUtils.isEmpty(ini)) {
                  log.debug("Discovered non-empty INI configuration at location '{}'.Using for configuration.",
                            location);
                  break;
                }
            }
      }
      return ini;
    }
    // DEFAULT_WEB_INI_RESOURCE_PATH : /WEB-INF/shiro.ini
    // IniFactorySupport.DEFAULT_INI_RESOURCE_PATH :classpath:shiro.ini
    protected String[] getDefaultConfigLocations() {
      return new String[]{
            DEFAULT_WEB_INI_RESOURCE_PATH,
            IniFactorySupport.DEFAULT_INI_RESOURCE_PATH
      };
    }
    // 创建FilterChain包装器
    protected FilterChainResolver createFilterChainResolver() {
      FilterChainResolver resolver = null;
      // 已经setIni过了,因此这里能够取到有效的ini实例
      Ini ini = getIni();
      if (!CollectionUtils.isEmpty(ini)) {
            // 如果ini文件中配置了filters或者urls,创建resolver
            //only create a resolver if the 'filters' or 'urls' sections are defined:
            // 判断ini文件中是否包含urls元素
            Ini.Section urls = ini.getSection(IniFilterChainResolverFactory.URLS);
            // 判断ini文件中是否包含filters元素
            Ini.Section filters = ini.getSection(IniFilterChainResolverFactory.FILTERS);
            // 生成FilterChainResolver实例
            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();
            }
      }
      // PathMatchingFilterChainResolver
      return resolver;
    }
}  

  objects对象是在父类中定义的
package org.apache.shiro.env;
public class DefaultEnvironment implements NamedObjectEnvironment, Destroyable {
    public static final String DEFAULT_SECURITY_MANAGER_KEY = "securityManager";
    // 这里变量的定义是final
    protected final Map objects;
    private String securityManagerName;
    public DefaultEnvironment() {
      this(new ConcurrentHashMap());
    }
    @SuppressWarnings({"unchecked"})
    public DefaultEnvironment(Map seed) {
      this.securityManagerName = DEFAULT_SECURITY_MANAGER_KEY;
      if (seed == null) {
            throw new IllegalArgumentException("Backing map cannot be null.");
      }
      // final的变量可以在构造方法中进行赋值
      this.objects = (Map) seed;
    }
    // securityManager等对象都会存入到这个objects对象中
}  

  Apache Shiro学习笔记(二)身份验证获取SecurityManager


[*]  WebSecurityManager wsm = (WebSecurityManager)factory.getInstance();
  WebIniSecurityManagerFactory类的getInstance方法实际是父类的AbstractFactory的方法

package org.apache.shiro.util;
/**
* TODO - Class JavaDoc
*
* @since 1.0
*/
public abstract class AbstractFactory implements Factory {
    private boolean singleton;
    private T singletonInstance;
    public AbstractFactory() {
      this.singleton = true;
    }
    public boolean isSingleton() {
      return singleton;
    }
    public void setSingleton(boolean singleton) {
      this.singleton = singleton;
    }
    public T getInstance() {
      T instance;
      if (isSingleton()) {
            if (this.singletonInstance == null) {
                this.singletonInstance = createInstance();
            }
            instance = this.singletonInstance;
      } else {
            instance = createInstance();
      }
      if (instance == null) {
            String msg = "Factory 'createInstance' implementation returned a null object.";
            throw new IllegalStateException(msg);
      }
      return instance;
    }
    protected abstract T createInstance();
}  


[*]  调用子类的IniFactorySupport.createInstance方法

package org.apache.shiro.config;
public abstract class IniFactorySupport extends AbstractFactory {
    public T createInstance() {
      Ini ini = resolveIni();
      T instance;
      if (CollectionUtils.isEmpty(ini)) {
            log.debug("No populated Ini available.Creating a default instance.");
            instance = createDefaultInstance();
            if (instance == null) {
                String msg = getClass().getName() + " implementation did not return a default instance in " +
                        "the event of a null/empty Ini configuration.This is required to support the " +
                        "Factory interface.Please check your implementation.";
                throw new IllegalStateException(msg);
            }
      } else {
            log.debug("Creating instance from Ini [" + ini + "]");
            instance = createInstance(ini);
            if (instance == null) {
                String msg = getClass().getName() + " implementation did not return a constructed instance from " +
                        "the createInstance(Ini) method implementation.";
                throw new IllegalStateException(msg);
            }
      }
      return instance;
    }
      // 抽象类,需要调用子类的实现
    protected abstract T createInstance(Ini ini);
      // 抽象类,需要调用子类的实现
    protected abstract T createDefaultInstance();
}  


[*]  子类IniSecurityManagerFactory的createInstance与createDefaultInstance方法

package org.apache.shiro.config;
public class IniSecurityManagerFactory extends IniFactorySupport {
    protected SecurityManager createDefaultInstance() {
      return new DefaultSecurityManager();
    }
    protected SecurityManager createInstance(Ini ini) {
      if (CollectionUtils.isEmpty(ini)) {
            throw new NullPointerException("Ini argument cannot be null or empty.");
      }
      SecurityManager securityManager = createSecurityManager(ini);
      if (securityManager == null) {
            String msg = SecurityManager.class + " instance cannot be null.";
            throw new ConfigurationException(msg);
      }
      return securityManager;
    }
    private SecurityManager createSecurityManager(Ini ini) {
      Ini.Section mainSection = ini.getSection(MAIN_SECTION_NAME);
      if (CollectionUtils.isEmpty(mainSection)) {
            //try the default:
            mainSection = ini.getSection(Ini.DEFAULT_SECTION_NAME);
      }
      return createSecurityManager(ini, mainSection);
    }
   // 同样需要创建SecurityManager的实例
    @SuppressWarnings({"unchecked"})
    private SecurityManager createSecurityManager(Ini ini, Ini.Section mainSection) {
      // 注意:getInstance方法是由WebIniSecurityManagerFactory触发的,因此这里的createDefaults会先调用子类的createDefaults
      // 与Apache Shiro学习笔记(二)身份验证获取SecurityManager 不一致了
      Map defaults = createDefaults(ini, mainSection);
      Map objects = buildInstances(mainSection, defaults);
      SecurityManager securityManager = getSecurityManagerBean();
      boolean autoApplyRealms = isAutoApplyRealms(securityManager);
      if (autoApplyRealms) {
            //realms and realm factory might have been created - pull them out first so we can
            //initialize the securityManager:
            Collection realms = getRealms(objects);
            //set them on the SecurityManager
            if (!CollectionUtils.isEmpty(realms)) {
                applyRealmsToSecurityManager(realms, securityManager);
            }
      }
      return securityManager;
    }
}  


[*]  在父类中还是需要子类的WebIniSecurityManagerFactory.createDefaults方法,在这个方法中会加载Shiro的默认filter

package org.apache.shiro.web.config;
public class WebIniSecurityManagerFactory extends IniSecurityManagerFactory {
    @SuppressWarnings({"unchecked"})
    @Override
    protected Map createDefaults(Ini ini, Ini.Section mainSection) {
      // 调用IniSecurityManagerFactory.createDefaults
      Map defaults = super.createDefaults(ini, mainSection);
      //add the default filters:添加默认的拦截器,如authc、ssl等
      Map defaultFilters = DefaultFilter.createInstanceMap(null);
      defaults.putAll(defaultFilters);
      return defaults;
    }
}  


[*]  内置的拦截器基于枚举来实现

package org.apache.shiro.web.filter.mgt;
import org.apache.shiro.util.ClassUtils;
import org.apache.shiro.web.filter.authc.*;
import org.apache.shiro.web.filter.authz.*;
import org.apache.shiro.web.filter.session.NoSessionCreationFilter;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Enum representing all of the default Shiro Filter instances available to web applications.Each filter instance is
* typically accessible in configuration the {@link #name() name} of the enum constant.
*
* @since 1.0
*/
public enum DefaultFilter {
    anon(AnonymousFilter.class),
    authc(FormAuthenticationFilter.class),
    authcBasic(BasicHttpAuthenticationFilter.class),
    logout(LogoutFilter.class),
    noSessionCreation(NoSessionCreationFilter.class),
    perms(PermissionsAuthorizationFilter.class),
    port(PortFilter.class),
    rest(HttpMethodPermissionFilter.class),
    roles(RolesAuthorizationFilter.class),
    ssl(SslFilter.class),
    user(UserFilter.class);
    private final Class
页: [1]
查看完整版本: Apache Shiro学习笔记(七)IniWebEnvironment