Shiro源码分析-初始化-SecurityManager

来源:互联网 发布:网络推广思维导图 编辑:程序博客网 时间:2024/05/16 09:26
源码分析的第一篇以SecurityManager的初始化为题。 
根据ini配置文件初始化shiro的代码主要为两段: 
//解析ini文件为Ini对象Factory<SecurityManager> factory = new IniSecurityManagerFactory(        "classpath:shiro-config.ini");//根据Ini对象初始化SecurityManager对象SecurityManager securityManager = factory.getInstance();

ini文件格式说明请参考: 

http://zh.wikipedia.org/wiki/INI%E6%AA%94%E6%A1%88 
java解析ini的方式也比较多,有兴趣可以参考: 
http://my.oschina.net/tinyframework/blog/214309 
一、Shiro解析ini的步骤如下: 
1、org.apache.shiro.config.IniSecurityManagerFactory类构造方法: 
public IniSecurityManagerFactory(String iniResourcePath) {        this(Ini.fromResourcePath(iniResourcePath));    }
将ini文件解析交给Ini的静态方法fromResourcePath完成。并把解析后的Ini对象设置由自身持有 
public IniSecurityManagerFactory(Ini config) {        setIni(config);    }
2、org.apache.shiro.config.Ini类解析逻辑: 
public static Ini fromResourcePath(String resourcePath) throws ConfigurationException {    if (!StringUtils.hasLength(resourcePath)) {      throw new IllegalArgumentException("Resource Path argument cannot be null or empty.");    }    //此处新建Ini对象    Ini ini = new Ini();    ini.loadFromPath(resourcePath);    return ini;  }  //根据资源路径获取输入流,交给load方法进行处理  public void loadFromPath(String resourcePath) throws ConfigurationException {    InputStream is;    try {      is = ResourceUtils.getInputStreamForPath(resourcePath);    } catch (IOException e) {      throw new ConfigurationException(e);    }    load(is);  }
ResourceUtils.getInputStreamForPath(resourcePath);这里支持三种获取资源的方式:
classpathshiro-config.ini从类路径中查找ini配置urlhttp://....../shiro-config.ini从指定的url获取ini配置fileD:\shiro-config.ini从指定的文件路径获取ini配置3、对获取的资源输入流最终交给文本扫描器Scaner,执行过程代码片段: 
public void load(Scanner scanner) {    String sectionName = DEFAULT_SECTION_NAME;    StringBuilder sectionContent = new StringBuilder();    while (scanner.hasNextLine()) {      String rawLine = scanner.nextLine();      String line = StringUtils.clean(rawLine);      //此处跳过ini文件格式的注释及空值      if (line == null || line.startsWith(COMMENT_POUND) || line.startsWith(COMMENT_SEMICOLON)) {        //skip empty lines and comments:        continue;      }      //此处主要获取section部分,根据[]规则      String newSectionName = getSectionName(line);      if (newSectionName != null) {        //此处代码主要用于构造Section对象,并放进sections集合中        addSection(sectionName, sectionContent);        //reset the buffer for the new section:        sectionContent = new StringBuilder();        sectionName = newSectionName;        if (log.isDebugEnabled()) {          log.debug("Parsing " + SECTION_PREFIX + sectionName + SECTION_SUFFIX);        }      } else {        //normal line - add it to the existing content buffer:        sectionContent.append(rawLine).append("\n");      }    }    //finish any remaining buffered content:    addSection(sectionName, sectionContent);  }
上段代码主要是组装ini文件中的section。细心的同学会发现Section、Ini都是实现了Map接口。Section持有的LinkedHashMap容器实际上是当前section中的所有键值对,而Ini持有的LinkedHashMap容器实际上是所有section名称与section对象的键值对。 
至此,ini文件的解析已经完成,其ini中的内容已全部以map的形式存放在Ini实例中。 
至于保存的结果是什么样的呢?以下面的一段配置为例: 
[main]#authenticatorauthenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticatorauthenticationStrategy=org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategyauthenticator.authenticationStrategy=$authenticationStrategysecurityManager.authenticator=$authenticator
Ini的sections属性[key=main,value=Section对象],此Section对象内部的props保存了所有main部分key-value映射。目前都是String类型,实例化在下一步完成的。 
二、由Ini实例构造SecurityManager对象 
SecurityManager securityManager = factory.getInstance();
IniSecurityManagerFactory的继承关系为: 
IniSecurityManagerFactory->IniFactorySupport->AbstractFactory->Factory 
这里的getInstance方法由最上级的抽象类:org.apache.shiro.util.AbstractFactory提供,源码如下: 
//该方法只是用于判断是否单例(默认为单例)  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;  }  //由子类IniFactorySupport创建实例  protected abstract T createInstance();
IniFactorySupport的createInstance实现如下(省略了无关紧要的日志、判断代码): 
public T createInstance() {    //此处获取解析ini文件产生的Ini对象    Ini ini = resolveIni();    T instance;    if (CollectionUtils.isEmpty(ini)) {      //如果ini为空,则创建默认实例      instance = createDefaultInstance();    } else {      //根据ini对象创建实例      instance = createInstance(ini);    }    return instance;  }  protected abstract T createInstance(Ini ini);  protected abstract T createDefaultInstance();
上面两个抽象方法,则交给实现类IniSecurityManagerFactory完成了。 
先阅读createDefaultInstance方法源码 
protected SecurityManager createDefaultInstance() {        return new DefaultSecurityManager();    }

终于在这个不起眼的地方,看到了初始化最核心的接口SecurityManager了。稍微暂停一下,发个SecurityManager的类图: 


由此图可以看出来,SecurityManager继承了三个接口(认证、授权、session管理),认证授权是安全框架最核心的功能,而shiro提供了自身的session管理机制(这也是shiro的一大亮点)。图中除了DefaultSecurityManager,其它所有类都是抽象类,由此可看出,DefaultSecurityManager是作为默认的安全管理器。 
再来看new DefaultSecurityManager();做的事情 

public DefaultSecurityManager() {        super();        this.subjectFactory = new DefaultSubjectFactory();        this.subjectDAO = new DefaultSubjectDAO();    }
这里暂时不深究每个构造器所做的具体事情。有兴趣的同学,可一直观察DefaultSecurityManager的所有父类构造器的处理逻辑。 
类名称构造方法创建的默认对象DefaultSecurityManagerDefaultSubjectFactory,DefaultSubjectDAOSessionsSecurityManagerDefaultSessionManagerAuthorizingSecurityManagerModularRealmAuthorizerAuthenticatingSecurityManagerModularRealmAuthenticatorRealmSecurityManager无CachingSecurityManager无在上面的表格中已经清晰的描述了各个类所设置的默认对象,至于用途后面再讲解。

0 0