• Shiro 中的 SecurityUtils(转)


    在 Shiro 中 SecurityUtils 是一个抽象类。并且没有任何子类。在其中声明了一个静态属性,三个静态方法。

    静态属性 securityManager

    private static SecurityManager securityManager;
    

    用来存储当前应用中全局唯一的一个SecurityManager。

    有两个静态方法是为此静态属性服务器,也就是下面这两个:

    public static void setSecurityManager(SecurityManager securityManager) {
        SecurityUtils.securityManager = securityManager;
    }
    
    public static SecurityManager getSecurityManager() throws UnavailableSecurityManagerException {
        SecurityManager securityManager = ThreadContext.getSecurityManager();
        if (securityManager == null) {
            securityManager = SecurityUtils.securityManager;
        }
        if (securityManager == null) {
            String msg = "No SecurityManager accessible to the calling code, either bound to the " +
                    ThreadContext.class.getName() + " or as a vm static singleton.  This is an invalid application " +
                    "configuration.";
            throw new UnavailableSecurityManagerException(msg);
        }
        return securityManager;
    }
    

    getSubject 静态方法

    这个是 Shiro 中最核心的方法了,用来获取 Subject.

    public static Subject getSubject() {
        Subject subject = ThreadContext.getSubject();
        if (subject == null) {
            subject = (new Subject.Builder()).buildSubject();
            ThreadContext.bind(subject);
        }
        return subject;
    }
    

    上述方法中,第二行(Subject subject = ThreadContext.getSubject();)获取到的Subject其实是第五行(ThreadContext.bind(subject);)绑定的。

    如果没有之前的绑定则得到null,然后就会走第四行(subject = (new Subject.Builder()).buildSubject();)获取。步骤如下:

    1. 调用Subject.Builder类的无参构造方法。如下代码:
        public Builder() {
            this(SecurityUtils.getSecurityManager());
        }
    

    在这个无参构造方法中,以当前应用全局唯一的SecurityManager对象为参调用了构造方法。如下:

        public Builder(SecurityManager securityManager) {
            if (securityManager == null) {
                throw new NullPointerException("SecurityManager method argument cannot be null.");
            }
            this.securityManager = securityManager;
            this.subjectContext = newSubjectContextInstance();
            if (this.subjectContext == null) {
                throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " +
                        "cannot be null.");
            }
            this.subjectContext.setSecurityManager(securityManager);
        }
    

    其实这些都不重要,至此我们知道了 Subject.Builder 对象中的SecurityManager对象,其实就是当前应用全局唯一的SecurityManager对象。

    注:以后在Shiro中,只要看到SecurityManager对象,你就认为它是当前应用全局唯一的那个SecurityManager对象就行了。

    1. 调用 Subject.Builder 对象的buildSubject方法。
        public Subject buildSubject() {
            return this.securityManager.createSubject(this.subjectContext);
        }
    

    其实里面是调用了SecurityManager对象的createSubject方法的,至于那个subjectContext参数,我们可以暂时不用理会。(在不同的应用环境下subjectContext是不一样的,如Web环境下它默认是DefaultWebSubjectContext

    //  DefaultSecurityManager 中的 createSubject 方法
    public Subject createSubject(SubjectContext subjectContext) {
        //create a copy so we don't modify the argument's backing map:
        SubjectContext context = copy(subjectContext);
    
        //ensure that the context has a SecurityManager instance, and if not, add one:
        context = ensureSecurityManager(context);
    
        //Resolve an associated Session (usually based on a referenced session ID), and place it in the context before
        //sending to the SubjectFactory.  The SubjectFactory should not need to know how to acquire sessions as the
        //process is often environment specific - better to shield the SF from these details:
        context = resolveSession(context);
    
        //Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first
        //if possible before handing off to the SubjectFactory:
        context = resolvePrincipals(context);
    
        Subject subject = doCreateSubject(context);
    
        //save this subject for future reference if necessary:
        //(this is needed here in case rememberMe principals were resolved and they need to be stored in the
        //session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation).
        //Added in 1.2:
        save(subject);
    
        return subject;
    }
    

    复制SubjectContext 对象之后执行的 context = ensureSecurityManager(context);是为了确保在SubjectContext对象中已经注入了当前应该用全局唯一的SecurityManager对象:

    //  DefaultSecurityManager 中的 ensureSecurityManager 方法
    protected SubjectContext ensureSecurityManager(SubjectContext context) {
        if (context.resolveSecurityManager() != null) {
            log.trace("Context already contains a SecurityManager instance.  Returning.");
            return context;
        }
        log.trace("No SecurityManager found in context.  Adding self reference.");
        context.setSecurityManager(this);
        return context;
    }
    

    也就是说,在SubjectContext中的SecurityManager也正是当前应该用全局唯一的SecurityManager对象。

    createSubject方法可知,在创建 Subject 前完成了以下两步工作:

    1. 解析了Sessioncontext = resolveSession(context););
    2. 解析了Principalscontext = resolvePrincipals(context);)。

    创建Subject之后,又执行了save(subject);。如果继续扒代码你会发现,这一步其实是把Subject存储到了Session中。具体代码如下所示:

    // DefaultSecurityManager 中的 save 方法
    protected void save(Subject subject) {
        this.subjectDAO.save(subject);
    }
    
    // SubjectDAO 接口的默认实现类 DefaultSubjectDAO 中的 save 方法
    public Subject save(Subject subject) {
        if (isSessionStorageEnabled(subject)) {
            saveToSession(subject);
        } else {
            log.trace("Session storage of subject state for Subject [{}] has been disabled: identity and " +
                    "authentication state are expected to be initialized on every request or invocation.", subject);
        }
    
        return subject;
    }
    
    // SubjectDAO 接口的默认实现类 DefaultSubjectDAO 中的 saveToSession方法
    protected void saveToSession(Subject subject) {
        //performs merge logic, only updating the Subject's session if it does not match the current state:
        mergePrincipals(subject);
        mergeAuthenticationState(subject);
    }


    作者:JSON_NULL
    链接:https://www.jianshu.com/p/cf95e3468638
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    将"089,0760,009"变为 89,760,9
    单向循环链表
    双链表复习
    【C语言】scanf()输入浮点型数据
    【C语言】一元二次方程(求实根和虚根)
    输入一个三位正整数,输出百位数,十位数,个位数
    输入身份证号,输出出生日期
    比较四个数的大小
    比较三个数的大小
    比较两个数的大小
  • 原文地址:https://www.cnblogs.com/muxi0407/p/11987661.html
Copyright © 2020-2023  润新知