Shiro使用和源码分析---5

getSubject分析

上一章看完了DefaultWebSecurityManager的构造函数,首先来分析getSubject函数。getSubject定义在AccessControlFilter中。

getSubject

    protected Subject getSubject(ServletRequest request, ServletResponse response) {
        return SecurityUtils.getSubject();
    }

再看SecurityUtils的getSubject函数,

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

ThreadContext相当于一个线程安全的类,ThreadContext的getSubject函数定义如下

    public static Subject getSubject() {
        return (Subject) get(SUBJECT_KEY);
    }

这里的SUBJECT_KEY=ThreadContext.class.getName() + “_SUBJECT_KEY”;,其get函数定义在ThreadContext中,如下

    public static Object get(Object key) {
        if (log.isTraceEnabled()) {
            String msg = "get() - in thread [" + Thread.currentThread().getName() + "]";
            log.trace(msg);
        }

        Object value = getValue(key);
        if ((value != null) && log.isTraceEnabled()) {
            String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" +
                    key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]";
            log.trace(msg);
        }
        return value;
    }

getValue函数定义在ThreadContext中,如下

    private static Object getValue(Object key) {
        Map<Object, Object> perThreadResources = resources.get();
        return perThreadResources != null ? perThreadResources.get(key) : null;
    }

这里假设是第一次访问该Subject,返回null。回到getSubject函数,这里就需要调用buildSubject函数,首先看Builder的构造函数,定义在Subject接口中,如下

        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);
        }

该构造函数就是简单的调用了newSubjectContextInstance,定义在Subject接口中,如下

        protected SubjectContext newSubjectContextInstance() {
            return new DefaultSubjectContext();
        }

DefaultSubjectContext的构造函数没有什么内容。回到getSubject函数中,接下来看buildSubject,如下

        public Subject buildSubject() {
            return this.securityManager.createSubject(this.subjectContext);
        }

这里的securityManager就是前面构造的DefaultWebSecurityManager啦,subjectContext=DefaultSubjectContext。
createSubject定义在DefaultSecurityManager中,

    public Subject createSubject(SubjectContext subjectContext) {
        SubjectContext context = copy(subjectContext);
        context = ensureSecurityManager(context);
        context = resolveSession(context);
        context = resolvePrincipals(context);
        Subject subject = doCreateSubject(context);
        save(subject);
        return subject;
    }

下面一一分析这些函数。

copy

    protected SubjectContext copy(SubjectContext subjectContext) {
        return new DefaultSubjectContext(subjectContext);
    }

DefaultSubjectContext的构造函数就是简单的将SubjectContext 赋值。

ensureSecurityManager

ensureSecurityManager定义在DefaultSecurityManager中,

    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,如果没有就进行设置。该context就是刚刚构造的DefaultSubjectContext,它的resolveSecurityManager如下

    public SecurityManager resolveSecurityManager() {
        SecurityManager securityManager = getSecurityManager();
        if (securityManager == null) {
            if (log.isDebugEnabled()) {
                log.debug("No SecurityManager available in subject context map.  " +
                        "Falling back to SecurityUtils.getSecurityManager() lookup.");
            }
            try {
                securityManager = SecurityUtils.getSecurityManager();
            } catch (UnavailableSecurityManagerException e) {
                if (log.isDebugEnabled()) {
                    log.debug("No SecurityManager available via SecurityUtils.  Heuristics exhausted.", e);
                }
            }
        }
        return securityManager;
    }

这个函数首先从该SubjectContext里取出SecurityManager,如果没有,就调用SecurityUtils的getSecurityManager从全局中取。显然这个函数再第一次调用的时候是找不到SecurityManager的,因此要返回到ensureSecurityManager进行设置。这样,执行完ensureSecurityManager后,SecurityManager和SubjectContext就关联起来了。

resolveSession

resolveSession定义在DefaultSecurityManager中,

    protected SubjectContext resolveSession(SubjectContext context) {
        if (context.resolveSession() != null) {
            log.debug("Context already contains a session.  Returning.");
            return context;
        }
        try {
            Session session = resolveContextSession(context);
            if (session != null) {
                context.setSession(session);
            }
        } catch (InvalidSessionException e) {
            log.debug("Resolved SubjectContext context session is invalid.  Ignoring and creating an anonymous " +
                    "(session-less) Subject instance.", e);
        }
        return context;
    }

这个函数和上面的ensureSecurityManager类似,首先调用resolveSession
resolveSession定义在DefaultSubjectContext中,

    public Session resolveSession() {
        Session session = getSession();
        if (session == null) {
            Subject existingSubject = getSubject();
            if (existingSubject != null) {
                session = existingSubject.getSession(false);
            }
        }
        return session;
    }

这个函数首先从该SubjectContext中获取Session,如果没有,就获取Subject,再从Subject中获取Session,如果找到则返回,否则返回null。因为是第一次调用该函数,所以肯定为null,所以回到resolveSession函数中,接下来进入resolveContextSession函数,构造一个Session,并和该SubjectContext相关联。
resolveContextSession定义在DefaultSecurityManager中,

    protected Session resolveContextSession(SubjectContext context) throws InvalidSessionException {
        SessionKey key = getSessionKey(context);
        if (key != null) {
            return getSession(key);
        }
        return null;
    }
    protected SessionKey getSessionKey(SubjectContext context) {
        Serializable sessionId = context.getSessionId();
        if (sessionId != null) {
            return new DefaultSessionKey(sessionId);
        }
        return null;
    }

这里首先获取sessionId ,这里第一次调用也为null,因此直接返回null。因此resolveSession啥也没干。

resolvePrincipals

resolvePrincipals定义在DefaultSecurityManager中,

    protected SubjectContext resolvePrincipals(SubjectContext context) {

        PrincipalCollection principals = context.resolvePrincipals();

        if (CollectionUtils.isEmpty(principals)) {
            log.trace("No identity (PrincipalCollection) found in the context.  Looking for a remembered identity.");

            principals = getRememberedIdentity(context);

            if (!CollectionUtils.isEmpty(principals)) {
                log.debug("Found remembered PrincipalCollection.  Adding to the context to be used " +
                        "for subject construction by the SubjectFactory.");

                context.setPrincipals(principals);
            } else {
                log.trace("No remembered identity found.  Returning original context.");
            }
        }

        return context;
    }

首先通过resolvePrincipals尝试获取PrincipalCollection,resolvePrincipals定义在DefaultSubjectContext中,

    public PrincipalCollection resolvePrincipals() {
        PrincipalCollection principals = getPrincipals();

        if (CollectionUtils.isEmpty(principals)) {
            AuthenticationInfo info = getAuthenticationInfo();
            if (info != null) {
                principals = info.getPrincipals();
            }
        }

        if (CollectionUtils.isEmpty(principals)) {
            Subject subject = getSubject();
            if (subject != null) {
                principals = subject.getPrincipals();
            }
        }

        if (CollectionUtils.isEmpty(principals)) {
            Session session = resolveSession();
            if (session != null) {
                principals = (PrincipalCollection) session.getAttribute(PRINCIPALS_SESSION_KEY);
            }
        }

        return principals;
    }

该代码依次尝试从SubjectContext、AuthenticationInfo、Subject和Session中获取PrincipalCollection。这里显然为null,因此返回到resolvePrincipals中,通过getRememberedIdentity获取。

getRememberedIdentity定义在DefaultSecurityManager中,

    protected PrincipalCollection getRememberedIdentity(SubjectContext subjectContext) {
        RememberMeManager rmm = getRememberMeManager();
        if (rmm != null) {
            try {
                return rmm.getRememberedPrincipals(subjectContext);
            } catch (Exception e) {
                if (log.isWarnEnabled()) {
                    String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() +
                            "] threw an exception during getRememberedPrincipals().";
                    log.warn(msg, e);
                }
            }
        }
        return null;
    }

getRememberMeManager首先从DefaultWebSecurityManager中获取RememberMeManager,在前面分析的DefaultWebSecurityManager构造函数中可知,getRememberMeManager将会返回CookieRememberMeManager,因此下面看getRememberedPrincipals函数,getRememberedPrincipals定义在CookieRememberMeManager的父类AbstractRememberMeManager中,

    public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {
        PrincipalCollection principals = null;
        try {
            byte[] bytes = getRememberedSerializedIdentity(subjectContext);
            if (bytes != null && bytes.length > 0) {
                principals = convertBytesToPrincipals(bytes, subjectContext);
            }
        } catch (RuntimeException re) {
            principals = onRememberedPrincipalFailure(re, subjectContext);
        }

        return principals;
    }

getRememberedSerializedIdentity定义在CookieRememberMeManager中,

    protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) {

        if (!WebUtils.isHttp(subjectContext)) {
            if (log.isDebugEnabled()) {
                String msg = "SubjectContext argument is not an HTTP-aware instance.  This is required to obtain a " +
                        "servlet request and response in order to retrieve the rememberMe cookie. Returning " +
                        "immediately and ignoring rememberMe operation.";
                log.debug(msg);
            }
            return null;
        }

        WebSubjectContext wsc = (WebSubjectContext) subjectContext;
        if (isIdentityRemoved(wsc)) {
            return null;
        }

        HttpServletRequest request = WebUtils.getHttpRequest(wsc);
        HttpServletResponse response = WebUtils.getHttpResponse(wsc);

        String base64 = getCookie().readValue(request, response);
        if (Cookie.DELETED_COOKIE_VALUE.equals(base64)) return null;

        if (base64 != null) {
            base64 = ensurePadding(base64);
            if (log.isTraceEnabled()) {
                log.trace("Acquired Base64 encoded identity [" + base64 + "]");
            }
            byte[] decoded = Base64.decode(base64);
            if (log.isTraceEnabled()) {
                log.trace("Base64 decoded byte array length: " + (decoded != null ? decoded.length : 0) + " bytes.");
            }
            return decoded;
        } else {
            return null;
        }
    }

这里传入的参数并不是WebSubjectContext,因此会在第一个if内直接返回。因此层层往上返回到resolvePrincipals,最后啥也没做。

doCreateSubject

    protected Subject doCreateSubject(SubjectContext context) {
        return getSubjectFactory().createSubject(context);
    }

getSubjectFactory()返回前面构造的DefaultWebSubjectFactory,因此createSubject定义在DefaultWebSubjectFactory中如下

    public Subject createSubject(SubjectContext context) {
        if (!(context instanceof WebSubjectContext)) {
            return super.createSubject(context);
        }
        WebSubjectContext wsc = (WebSubjectContext) context;
        SecurityManager securityManager = wsc.resolveSecurityManager();
        Session session = wsc.resolveSession();
        boolean sessionEnabled = wsc.isSessionCreationEnabled();
        PrincipalCollection principals = wsc.resolvePrincipals();
        boolean authenticated = wsc.resolveAuthenticated();
        String host = wsc.resolveHost();
        ServletRequest request = wsc.resolveServletRequest();
        ServletResponse response = wsc.resolveServletResponse();

        return new WebDelegatingSubject(principals, authenticated, host, session, sessionEnabled,
                request, response, securityManager);
    }

因为这时context并不是WebSubjectContext,因此直接调用父类DefaultSubjectFactory的createSubject函数,

    public Subject createSubject(SubjectContext context) {
        SecurityManager securityManager = context.resolveSecurityManager();
        Session session = context.resolveSession();
        boolean sessionCreationEnabled = context.isSessionCreationEnabled();
        PrincipalCollection principals = context.resolvePrincipals();
        boolean authenticated = context.resolveAuthenticated();
        String host = context.resolveHost();

        return new DelegatingSubject(principals, authenticated, host, session, sessionCreationEnabled, securityManager);
    }

这里大部分参数都为null,然后直接调用了DelegatingSubject的构造函数。因此,回到doCreateSubject函数,这里就是构造了一个DelegatingSubject函数。

save

    protected void save(Subject subject) {
        this.subjectDAO.save(subject);
    }

这里的subjectDAO是在前面构造函数中构造的DefaultSubjectDAO,因此看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;
    }

isSessionStorageEnabled也定义在DefaultSubjectDAO中,

    protected boolean isSessionStorageEnabled(Subject subject) {
        return getSessionStorageEvaluator().isSessionStorageEnabled(subject);
    }

getSessionStorageEvaluator返回在DefaultSubjectDAO的构造函数中构造的DefaultSessionStorageEvaluator,因此接着看它的isSessionStorageEnabled函数,

    public boolean isSessionStorageEnabled(Subject subject) {
        return (subject != null && subject.getSession(false) != null) || isSessionStorageEnabled();
    }

这里isSessionStorageEnabled默认的返回true,因此回到save中,接着看saveToSession,

    protected void saveToSession(Subject subject) {
        mergePrincipals(subject);
        mergeAuthenticationState(subject);
    }

该函数就是将Subject的状态存在Session中,首先看mergePrincipals函数,

    protected void mergePrincipals(Subject subject) {

        PrincipalCollection currentPrincipals = null;

        if (subject.isRunAs() && subject instanceof DelegatingSubject) {
            try {
                Field field = DelegatingSubject.class.getDeclaredField("principals");
                field.setAccessible(true);
                currentPrincipals = (PrincipalCollection)field.get(subject);
            } catch (Exception e) {
                throw new IllegalStateException("Unable to access DelegatingSubject principals property.", e);
            }
        }
        if (currentPrincipals == null || currentPrincipals.isEmpty()) {
            currentPrincipals = subject.getPrincipals();
        }

        Session session = subject.getSession(false);

        if (session == null) {
            if (!CollectionUtils.isEmpty(currentPrincipals)) {
                session = subject.getSession();
                session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, currentPrincipals);
            }
        } else {
            PrincipalCollection existingPrincipals =
                    (PrincipalCollection) session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);

            if (CollectionUtils.isEmpty(currentPrincipals)) {
                if (!CollectionUtils.isEmpty(existingPrincipals)) {
                    session.removeAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
                }
            } else {
                if (!currentPrincipals.equals(existingPrincipals)) {
                    session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, currentPrincipals);
                }
            }
        }
    }

首先,在前面doCreateSubject中构造的确实是一个DelegatingSubject,这里isRunAs会返回false,至于为什么,这里就不分析了。下面会进入第二个if,currentPrincipals会被赋值为null(查看之前构造的DelegatingSubject)。下面看Subject的getSession函数,

    public Session getSession(boolean create) {
        if (log.isTraceEnabled()) {
            log.trace("attempting to get session; create = " + create +
                    "; session is null = " + (this.session == null) +
                    "; session has id = " + (this.session != null && session.getId() != null));
        }

        if (this.session == null && create) {
            if (!isSessionCreationEnabled()) {
                String msg = "Session creation has been disabled for the current subject.  This exception indicates " +
                        "that there is either a programming error (using a session when it should never be " +
                        "used) or that Shiro's configuration needs to be adjusted to allow Sessions to be created " +
                        "for the current Subject.  See the " + DisabledSessionException.class.getName() + " JavaDoc " +
                        "for more.";
                throw new DisabledSessionException(msg);
            }

            log.trace("Starting session for host {}", getHost());
            SessionContext sessionContext = createSessionContext();
            Session session = this.securityManager.start(sessionContext);
            this.session = decorate(session);
        }
        return this.session;
    }

这里传入的参数create为false,因此不会构造session,因此返回null。本章最后假设create为true,分析如何构造该Session。返回到mergePrincipals函数,因为session和currentPrincipals都为null,因此直接返回。所以第一次进入mergePrincipals函数,什么都没做。
回到saveToSession函数,再看mergeAuthenticationState函数,

    protected void mergeAuthenticationState(Subject subject) {

        Session session = subject.getSession(false);

        if (session == null) {
            if (subject.isAuthenticated()) {
                session = subject.getSession();
                session.setAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY, Boolean.TRUE);
            }
        } else {
            Boolean existingAuthc = (Boolean) session.getAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);

            if (subject.isAuthenticated()) {
                if (existingAuthc == null || !existingAuthc) {
                    session.setAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY, Boolean.TRUE);
                }
            } else {
                if (existingAuthc != null) {
                    session.removeAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);
                }
            }
        }
    }

和前面的分析类似,这里也什么也没做。

bind

回到最前头看SecurityUtils的getSubject函数,最后调用了ThreadContext的bind函数,

    public static void bind(Subject subject) {
        if (subject != null) {
            put(SUBJECT_KEY, subject);
        }
    }

看一下put,定义在ThreadContext中,

    public static void put(Object key, Object value) {
        if (key == null) {
            throw new IllegalArgumentException("key cannot be null");
        }

        if (value == null) {
            remove(key);
            return;
        }

        ensureResourcesInitialized();
        resources.get().put(key, value);

        if (log.isTraceEnabled()) {
            String msg = "Bound value of type [" + value.getClass().getName() + "] for key [" +
                    key + "] to thread " + "[" + Thread.currentThread().getName() + "]";
            log.trace(msg);
        }
    }

ensureResourcesInitialized就是初始化resources了,

    private static void ensureResourcesInitialized(){
        if (resources.get() == null){
           resources.set(new HashMap<Object, Object>());
        }
    }

因此最后就是简单地将刚刚构造的Subject(其实是DelegatingSubject)放入resources中。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值