`
Dead_knight
  • 浏览: 1202218 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
博客专栏
752c8642-b795-3fe6-946e-a4e845bffdec
Spring Securi...
浏览量:240599
33caa84e-18a6-3036-a82b-6e2106a4de63
clojure专题
浏览量:49035
E17ca077-44df-3816-a3fe-471c43f6e1e5
WebLogic11g
浏览量:237060
社区版块
存档分类
最新评论

Shiro源码分析-初始化-SecurityManager

阅读更多
开涛的《跟我学Shiro》系列已即将完成,该系列囊括了shiro的绝大部分实用功能,并且在讲解如何用的过程中,也添加了其内部实现的原理。开涛主要以Shiro的使用者为角度,所以其原理部分是穿插在各个章节内的。

源码分析的第一篇以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]
#authenticator
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
authenticationStrategy=org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy
authenticator.authenticationStrategy=$authenticationStrategy
securityManager.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,DefaultSubjectDAO
SessionsSecurityManagerDefaultSessionManager
AuthorizingSecurityManagerModularRealmAuthorizer
AuthenticatingSecurityManagerModularRealmAuthenticator
RealmSecurityManager
CachingSecurityManager

在上面的表格中已经清晰的描述了各个类所设置的默认对象,至于用途后面再讲解。需要注意的是,RealmSecurityManager、CachingSecurityManager并没有设置默认的对象,所以这个是交给开发人员自己配置的。

接下来看createInstance实现细节:
    protected SecurityManager createInstance(Ini ini) {
        if (CollectionUtils.isEmpty(ini)) {
            throw new NullPointerException("Ini argument cannot be null or empty.");
        }
        //SecurityManager的创建工作交给createSecurityManager方法
        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) {
        //单独获取main部分进行初始化
        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);
    }

    private SecurityManager createSecurityManager(Ini ini, Ini.Section mainSection) {
        //创建DefaultSecurityManager,并且根据ini初始化realm
        Map<String, ?> defaults = createDefaults(ini, mainSection);
        //根据main部分构造对象(包括属性依赖,创建实例等)
        Map<String, ?> objects = buildInstances(mainSection, defaults);
        //从ReflectionBuilder的objects获取SecurityManager实例。ReflectionBuilder负责shiro的所有反射构造实例的工作,并且把实例放在objects的集合中持有。
        SecurityManager securityManager = getSecurityManagerBean();
        //如果显示指定了realm,则不会自动配置realm。
        boolean autoApplyRealms = isAutoApplyRealms(securityManager);

        if (autoApplyRealms) {
            //如果在ini文件中未显示指定realm(即:securityManager.realms=$myRealm1)
            //那么ini配置的所有realm会自动设置(即:realm1=test.Realm1)
            //getRealms方法只是把ini配置的所有实例对象中实现Realm、RealmFactory接口的实例添加到SecurityManager中
            Collection<Realm> realms = getRealms(objects);
            //set them on the SecurityManager
            if (!CollectionUtils.isEmpty(realms)) {
                applyRealmsToSecurityManager(realms, securityManager);
            }
        }

        return securityManager;
    }

    //创建默认的SecurityManager
    protected Map<String, ?> createDefaults(Ini ini, Ini.Section mainSection) {
        Map<String, Object> defaults = new LinkedHashMap<String, Object>();
        //这里还是先创建了DefaultSecurityManager
        SecurityManager securityManager = createDefaultInstance();
        defaults.put(SECURITY_MANAGER_NAME, securityManager);
        //判断ini是否配置了roles、users
        if (shouldImplicitlyCreateRealm(ini)) {
            //如果配置了roles或users,则创建IniRealm
            Realm realm = createRealm(ini);
            if (realm != null) {
                defaults.put(INI_REALM_NAME, realm);
            }
        }

        return defaults;
    }
    //对main部分配置的所有类型、属性进行实例化,并设置依赖,放到objects集合中
    private Map<String, ?> buildInstances(Ini.Section section, Map<String, ?> defaults) {
        this.builder = new ReflectionBuilder(defaults);
        return this.builder.buildObjects(section);
    }


再看看ReflectionBuilder如何对main进行实例化及依赖的设置:
    public Map<String, ?> buildObjects(Map<String, String> kvPairs) {
        if (kvPairs != null && !kvPairs.isEmpty()) {
            //实例配置集合
            Map<String, String> instanceMap = new LinkedHashMap<String, String>();
            //属性依赖配置集合
            Map<String, String> propertyMap = new LinkedHashMap<String, String>();

            for (Map.Entry<String, String> entry : kvPairs.entrySet()) {
                //如果ini配置的key包括.号,或者以.class后缀的名称,则认为类型配置
                if (entry.getKey().indexOf('.') < 0 || entry.getKey().endsWith(".class")) {
                    instanceMap.put(entry.getKey(), entry.getValue());
                //其它情况,则认为是属性依赖配置
                } else {
                    propertyMap.put(entry.getKey(), entry.getValue());
                }
            }

            // Create all instances
            for (Map.Entry<String, String> entry : instanceMap.entrySet()) {
                //实例化每个类型
                createNewInstance((Map<String, Object>) objects, entry.getKey(), entry.getValue());
            }

            // Set all properties
            for (Map.Entry<String, String> entry : propertyMap.entrySet()) {
                //配置所有的依赖。必须有个先后顺序,先实例化再设置依赖
                applyProperty(entry.getKey(), entry.getValue(), objects);
            }
        }

        //SHIRO-413: init method must be called for constructed objects that are Initializable
        //如果配置的类型实现了Initializable接口,则调用其init方法
        LifecycleUtils.init(objects.values());

        return objects;
    }


SecurityManager的初始化部分已经完成。
5
1
分享到:
评论
2 楼 Dead_knight 2014-04-04  
yzwcroco 写道
请问类图是用什么工具画的?

idea的工具
1 楼 yzwcroco 2014-04-04  
请问类图是用什么工具画的?

相关推荐

    shiro(shiro-all-1.8.0.jar)

    shiro(shiro-all-1.8.0.jar)

    shiro源码(shiro-root-1.8.0-source-release.zip)

    Shiro的模块化设计允许开发者通过插件扩展其功能,如自定义认证策略、加密算法等。 10. **事件监听**: Shiro提供了事件监听机制,开发者可以通过实现Listener接口,对认证、授权等事件进行监听和处理。 11. **...

    shiro-core-1.4.0-API文档-中文版.zip

    赠送jar包:shiro-core-1.4.0.jar; 赠送原API文档:shiro-core-1.4.0-javadoc.jar; 赠送源代码:shiro-core-1.4.0-sources.jar; 赠送Maven依赖信息文件:shiro-core-1.4.0.pom; 包含翻译后的API文档:shiro-core...

    shiro-attack-4.7.0-SNAPSHOT-all.zip

    标题提到的"shiro-attack-4.7.0-SNAPSHOT-all.zip"很可能是针对Apache Shiro的安全测试工具或者漏洞利用工具包,其主要目的是帮助开发者检测和防范Shiro框架相关的安全问题。 描述中的"序列化验证工具"可能是指该...

    shiro-config-core-1.4.0-API文档-中文版.zip

    赠送jar包:shiro-config-core-1.4.0.jar; 赠送原API文档:shiro-config-core-1.4.0-javadoc.jar; 赠送源代码:shiro-config-core-1.4.0-sources.jar; 赠送Maven依赖信息文件:shiro-config-core-1.4.0.pom; ...

    shiro-ehcache-1.4.0-API文档-中文版.zip

    赠送jar包:shiro-ehcache-1.4.0.jar; 赠送原API文档:shiro-ehcache-1.4.0-javadoc.jar; 赠送源代码:shiro-ehcache-1.4.0-sources.jar; 赠送Maven依赖信息文件:shiro-ehcache-1.4.0.pom; 包含翻译后的API文档...

    shiro-jar-1.7.0.zip相关资源包

    解决:升級1.7后附件...shiro-cas-1.7.0.jar shiro-core-1.7.0.jar shiro-ehcache-1.7.0.jar shiro-spring-1.7.0.jar shiro-web-1.7.0.jar CustomShiroFilterFactoryBean.java spring-context-shiro.xml 修改说明.txt

    shiro-jar.zip

    shiro-all-1.7.1.jar,shiro-aspectj-1.7.1.jar,shiro-cache-1.7.1.jar,shiro-config-core-1.7.1.jar,shiro-config-ogdl-1.7.1.jar,shiro-core-1.7.1.jar,shiro-crypto-cipher-1.7.1.jar,shiro-crypto-core-1.7.1.jar...

    shiro-crypto-core-1.4.0-API文档-中文版.zip

    赠送jar包:shiro-crypto-core-1.4.0.jar; 赠送原API文档:shiro-crypto-core-1.4.0-javadoc.jar; 赠送源代码:shiro-crypto-core-1.4.0-sources.jar; 赠送Maven依赖信息文件:shiro-crypto-core-1.4.0.pom; ...

    shiro-crypto-cipher-1.4.0-API文档-中文版.zip

    赠送jar包:shiro-crypto-cipher-1.4.0.jar; 赠送原API文档:shiro-crypto-cipher-1.4.0-javadoc.jar; 赠送源代码:shiro-crypto-cipher-1.4.0-sources.jar; 赠送Maven依赖信息文件:shiro-crypto-cipher-1.4.0....

    shiro-crypto-core-1.4.0-API文档-中英对照版.zip

    赠送jar包:shiro-crypto-core-1.4.0.jar; 赠送原API文档:shiro-crypto-core-1.4.0-javadoc.jar; 赠送源代码:shiro-crypto-core-1.4.0-sources.jar; 赠送Maven依赖信息文件:shiro-crypto-core-1.4.0.pom; ...

    shiro-config-core-1.4.0-API文档-中英对照版.zip

    赠送jar包:shiro-config-core-1.4.0.jar; 赠送原API文档:shiro-config-core-1.4.0-javadoc.jar; 赠送源代码:shiro-config-core-1.4.0-sources.jar; 赠送Maven依赖信息文件:shiro-config-core-1.4.0.pom; ...

    shiro-config-ogdl-1.4.0-API文档-中英对照版.zip

    赠送jar包:shiro-config-ogdl-1.4.0.jar; 赠送原API文档:shiro-config-ogdl-1.4.0-javadoc.jar; 赠送源代码:shiro-config-ogdl-1.4.0-sources.jar; 赠送Maven依赖信息文件:shiro-config-ogdl-1.4.0.pom; ...

    shiro-cas-1.2.3-API文档-中文版.zip

    赠送jar包:shiro-cas-1.2.3.jar; 赠送原API文档:shiro-cas-1.2.3-javadoc.jar; 赠送源代码:shiro-cas-1.2.3-sources.jar; 赠送Maven依赖信息文件:shiro-cas-1.2.3.pom; 包含翻译后的API文档:shiro-cas-...

    shiro-core-1.3.2-API文档-中文版.zip

    赠送jar包:shiro-core-1.3.2.jar; 赠送原API文档:shiro-core-1.3.2-javadoc.jar; 赠送源代码:shiro-core-1.3.2-sources.jar; 包含翻译后的API文档:shiro-core-1.3.2-javadoc-API文档-中文(简体)版.zip ...

    shiro-cas-1.2.3-API文档-中英对照版.zip

    赠送jar包:shiro-cas-1.2.3.jar; 赠送原API文档:shiro-cas-1.2.3-javadoc.jar; 赠送源代码:shiro-cas-1.2.3-sources.jar; 赠送Maven依赖信息文件:shiro-cas-1.2.3.pom; 包含翻译后的API文档:shiro-cas-...

    shiro-cache-1.4.0-API文档-中文版.zip

    赠送jar包:shiro-cache-1.4.0.jar; 赠送原API文档:shiro-cache-1.4.0-javadoc.jar; 赠送源代码:shiro-cache-1.4.0-sources.jar; 赠送Maven依赖信息文件:shiro-cache-1.4.0.pom; 包含翻译后的API文档:shiro-...

    shiro-core-1.7.1 jar

    shiro shiro-core-1.7.1 jar shiro漏洞

    shiro-cache-1.4.0-API文档-中英对照版.zip

    赠送jar包:shiro-cache-1.4.0.jar; 赠送原API文档:shiro-cache-1.4.0-javadoc.jar; 赠送源代码:shiro-cache-1.4.0-sources.jar; 赠送Maven依赖信息文件:shiro-cache-1.4.0.pom; 包含翻译后的API文档:shiro-...

    shiro-event-1.4.0-API文档-中文版.zip

    赠送jar包:shiro-event-1.4.0.jar; 赠送原API文档:shiro-event-1.4.0-javadoc.jar; 赠送源代码:shiro-event-1.4.0-sources.jar; 赠送Maven依赖信息文件:shiro-event-1.4.0.pom; 包含翻译后的API文档:shiro-...

Global site tag (gtag.js) - Google Analytics