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

来源:互联网 发布:淘宝手机话费充值平台 编辑:程序博客网 时间:2024/06/05 05:24

login和isAuthenticated函数分析

从上一章分析可知,调用SecurityUtils的getSubject函数后,最后是调用doCreateSubject函数构造了一个DelegatingSubject,因此这里直接看DelegatingSubject的login函数。

login

    public void login(AuthenticationToken token) throws AuthenticationException {        clearRunAsIdentitiesInternal();        Subject subject = securityManager.login(this, token);        PrincipalCollection principals;        String host = null;        if (subject instanceof DelegatingSubject) {            DelegatingSubject delegating = (DelegatingSubject) subject;            principals = delegating.principals;            host = delegating.host;        } else {            principals = subject.getPrincipals();        }        if (principals == null || principals.isEmpty()) {            String msg = "Principals returned from securityManager.login( token ) returned a null or " +                    "empty value.  This value must be non null and populated with one or more elements.";            throw new IllegalStateException(msg);        }        this.principals = principals;        this.authenticated = true;        if (token instanceof HostAuthenticationToken) {            host = ((HostAuthenticationToken) token).getHost();        }        if (host != null) {            this.host = host;        }        Session session = subject.getSession(false);        if (session != null) {            this.session = decorate(session);        } else {            this.session = null;        }    }

clearRunAsIdentitiesInternal定义在DelegatingSubject中

    private void clearRunAsIdentitiesInternal() {        try {            clearRunAsIdentities();        } catch (SessionException se) {            log.debug("Encountered session exception trying to clear 'runAs' identities during logout.  This " +                    "can generally safely be ignored.", se);        }    }

clearRunAsIdentities定义在DelegatingSubject中,

    private void clearRunAsIdentities() {        Session session = getSession(false);        if (session != null) {            session.removeAttribute(RUN_AS_PRINCIPALS_SESSION_KEY);        }    }

这里假设getSession依然返回null。后面的就是一些基本的设置工作了,本章最主要看一下login函数。

SecurityManager.login

回到login函数,securityManager是在上一章中构造的DefaultWebSecurityManager,其login函数如下

    public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {        AuthenticationInfo info;        try {            info = authenticate(token);        } catch (AuthenticationException ae) {            try {                onFailedLogin(token, ae, subject);            } catch (Exception e) {                if (log.isInfoEnabled()) {                    log.info("onFailedLogin method threw an " +                            "exception.  Logging and propagating original AuthenticationException.", e);                }            }            throw ae;        }        Subject loggedIn = createSubject(token, info, subject);        onSuccessfulLogin(token, info, loggedIn);        return loggedIn;    }

authenticate就是验证用户名和密码啦,定义在AuthenticatingSecurityManager中,

    public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {        return this.authenticator.authenticate(token);    }

authenticator是在AuthenticatingSecurityManager构造函数中实例化的ModularRealmAuthenticator,
ModularRealmAuthenticator继承自AbstractAuthenticator,authenticate定义在AbstractAuthenticator中,继续看

    public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {        if (token == null) {            throw new IllegalArgumentException("Method argumet (authentication token) cannot be null.");        }        log.trace("Authentication attempt received for token [{}]", token);        AuthenticationInfo info;        try {            info = doAuthenticate(token);            if (info == null) {                String msg = "No account information found for authentication token [" + token + "] by this " +                        "Authenticator instance.  Please check that it is configured correctly.";                throw new AuthenticationException(msg);            }        } catch (Throwable t) {            AuthenticationException ae = null;            if (t instanceof AuthenticationException) {                ae = (AuthenticationException) t;            }            if (ae == null) {                String msg = "Authentication failed for token submission [" + token + "].  Possible unexpected " +                        "error? (Typical or expected login exceptions should extend from AuthenticationException).";                ae = new AuthenticationException(msg, t);            }            try {                notifyFailure(token, ae);            } catch (Throwable t2) {                if (log.isWarnEnabled()) {                    String msg = "Unable to send notification for failed authentication attempt - listener error?.  " +                            "Please check your AuthenticationListener implementation(s).  Logging sending exception " +                            "and propagating original AuthenticationException instead...";                    log.warn(msg, t2);                }            }            throw ae;        }        log.debug("Authentication successful for token [{}].  Returned account [{}]", token, info);        notifySuccess(token, info);        return info;    }

doAuthenticate定义在ModularRealmAuthenticator中,

    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {        assertRealmsConfigured();        Collection<Realm> realms = getRealms();        if (realms.size() == 1) {            return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);        } else {            return doMultiRealmAuthentication(realms, authenticationToken);        }    }

getRealms返回Realms,这是在第四章DefaultWebSecurityManager构造函数的setRealm里设置的AuthorizingRealm。这里假设只有一个Realm,因此执行doSingleRealmAuthentication函数,如果有多个Realm会执行doMultiRealmAuthentication,doMultiRealmAuthentication的定义就不往下看了。doSingleRealmAuthentication定义在ModularRealmAuthenticator中,

    protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {        if (!realm.supports(token)) {            String msg = "Realm [" + realm + "] does not support authentication token [" +                    token + "].  Please ensure that the appropriate Realm implementation is " +                    "configured correctly or that the realm accepts AuthenticationTokens of this type.";            throw new UnsupportedTokenException(msg);        }        AuthenticationInfo info = realm.getAuthenticationInfo(token);        if (info == null) {            String msg = "Realm [" + realm + "] was unable to find account data for the " +                    "submitted AuthenticationToken [" + token + "].";            throw new UnknownAccountException(msg);        }        return info;    }

有关Realm的supports和getAuthenticationInfo等函数放在后面的章节分析,这里只有知道调用getAuthenticationInfo就是对用户名和密码进行验证,然后返回一个AuthenticationInfo。

回到AbstractAuthenticator的authenticate函数中,这里假设登录成功,就会执行一个函数notifySuccess函数,定义在AbstractAuthenticator中,

    protected void notifySuccess(AuthenticationToken token, AuthenticationInfo info) {        for (AuthenticationListener listener : this.listeners) {            listener.onSuccess(token, info);        }    }

这里就是设置一些监听器啦。

createSubject

回到SecurityManager的login函数中,接下来执行createSubject函数重新构造Subject,定义在DefaultSecurityManager中,

    protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing) {        SubjectContext context = createSubjectContext();        context.setAuthenticated(true);        context.setAuthenticationToken(token);        context.setAuthenticationInfo(info);        if (existing != null) {            context.setSubject(existing);        }        return createSubject(context);    }

这里的createSubjectContext定义在DefaultWebSecurityManager中,

    protected SubjectContext createSubjectContext() {        return new DefaultWebSubjectContext();    }

DefaultWebSubjectContext构造函数是空函数,这里不管它了。
接下来的三个set也都是简单的赋值,不管它了。这里传进来的existing是在上一章中构造的DelegatingSubject,setSubject也是简单的赋值,接着就调用createSubject构造一个新的Subject。createSubject函数在第五章已经分析过了,这里简单说一下重新构造的Subject有什么不同。

第一个不同:resolvePrincipals

因为这里是web相关的Subject,因此需要处理一些从客户端传过来的Cookie。在第五章的分析中,resolvePrincipals会调用getRememberedIdentity,getRememberedIdentity中会获取CookieRememberMeManager,并调用其getRememberedPrincipals函数,返回一个PrincipalCollection。下面来看,

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

在第五章的分析中,isHttp会返回false,因为当时构造的是一个DefaultSubjectContext,而这里是DefaultWebSubjectContext,isHttp返回true。
isIdentityRemoved判断请求request中是否包含了相应的属性,这里返回false。
getCookie()返回SimpleCookie,readValue查找客户端发来的Cookies中是否有name为rememberMe的Cookie,并从中获取值。后面就是一些判断,假设获取到该值,就通过Base64解码成byte数组并返回。
返回到getRememberedPrincipals中,接着调用convertBytesToPrincipals将byte数组解析成PrincipalCollection,这里不往下看了。获得了该PrincipalCollection后,层层往上返回到resolvePrincipals中,并将该PrincipalCollection设置进DefaultWebSubjectContext的backingMap中。

第二个不同:doCreateSubject

doCreateSubject会调用DefaultWebSubjectFactory的createSubject函数。这里的不同主要是在第五章的分析中,调用的是父类的构造函数构造了一个DelegatingSubject,而本章则是构造了一个WebDelegatingSubject。

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

首先注意这里的resolveAuthenticated会返回true,定义在DefaultSubjectContext中,

    public boolean resolveAuthenticated() {        Boolean authc = getTypedValue(AUTHENTICATED, Boolean.class);        if (authc == null) {            AuthenticationInfo info = getAuthenticationInfo();            authc = info != null;        }        if (!authc) {            Session session = resolveSession();            if (session != null) {                Boolean sessionAuthc = (Boolean) session.getAttribute(AUTHENTICATED_SESSION_KEY);                authc = sessionAuthc != null && sessionAuthc;            }        }        return authc;    }

resolveHost返回客户端主机名。

    public String resolveHost() {        String host = super.resolveHost();        if (host == null) {            ServletRequest request = resolveServletRequest();            if (request != null) {                host = request.getRemoteHost();            }        }        return host;    }

resolveServletRequest和resolveServletResponse类似,下面只分析resolveServletRequest。

    public ServletRequest resolveServletRequest() {        ServletRequest request = getServletRequest();        if (request == null) {            Subject existing = getSubject();            if (existing instanceof WebSubject) {                request = ((WebSubject) existing).getServletRequest();            }        }        return request;    }

这里首先调用getServletRequest尝试从backingMap中获取ServletRequest。如果返回null,则继续从backingMap获取Subject(在createSubject中设置的),从该Subject中获取ServletRequest。
回到createSubject,最后构造一个WebDelegatingSubject。

    public WebDelegatingSubject(PrincipalCollection principals, boolean authenticated,                                String host, Session session, boolean sessionEnabled,                                ServletRequest request, ServletResponse response,                                SecurityManager securityManager) {        super(principals, authenticated, host, session, sessionEnabled, securityManager);        this.servletRequest = request;        this.servletResponse = response;    }

父类的构造函数就是DelegatingSubject的构造函数了,在第五章已经分析过了。

第三个不同:save

save定义在DefaultSecurityManager中,继而会调用DefaultSubjectDAO的save函数,该函数调用saveToSession,saveToSession调用mergeAuthenticationState函数。和第五章不同的是,这里会创建Session。

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

这里的subject为前面构造的WebDelegatingSubject,这里调用其getSession函数构造一个Session,最终的函数如下

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

当调用无参的getSession时,这里传入的参数create为true,因此首先调用createSessionContext构造DefaultSessionContext。createSessionContext定义在WebDelegatingSubject中,

    protected SessionContext createSessionContext() {        WebSessionContext wsc = new DefaultWebSessionContext();        String host = getHost();        if (StringUtils.hasText(host)) {            wsc.setHost(host);        }        wsc.setServletRequest(this.servletRequest);        wsc.setServletResponse(this.servletResponse);        return wsc;    }

(很多人会问这里的this.servletRequest和this.servletResponse是在哪传入的,答案是AbstractShiroFilter的doFilterInternal中,在Subject的execute函数中会将构造的Subject设置进ThreadContext里)然后就调用DefaultWebSecurityManager的start函数构造Session,start定义在其父类SessionsSecurityManager中,

    public Session start(SessionContext context) throws AuthorizationException {        return this.sessionManager.start(context);    }

sessionManager是在构造函数中构造的ServletContainerSessionManager,

    public Session start(SessionContext context) throws AuthorizationException {        return createSession(context);    }
    protected Session createSession(SessionContext sessionContext) throws AuthorizationException {        if (!WebUtils.isHttp(sessionContext)) {            String msg = "SessionContext must be an HTTP compatible implementation.";            throw new IllegalArgumentException(msg);        }        HttpServletRequest request = WebUtils.getHttpRequest(sessionContext);        HttpSession httpSession = request.getSession();        String host = getHost(sessionContext);        return createSession(httpSession, host);    }
    protected Session createSession(HttpSession httpSession, String host) {        return new HttpServletSession(httpSession, host);    }

HttpServletSession只是对HttpSession的一个包装而已。

onSuccessfulLogin

回到SecurityManager.login中,最后调用onSuccessfulLogin回调函数,定义在DefaultSecurityManager中,

    protected void onSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject) {        rememberMeSuccessfulLogin(token, info, subject);    }
    protected void rememberMeSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject) {        RememberMeManager rmm = getRememberMeManager();        if (rmm != null) {            try {                rmm.onSuccessfulLogin(subject, token, info);            } catch (Exception e) {                if (log.isWarnEnabled()) {                    String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() +                            "] threw an exception during onSuccessfulLogin.  RememberMe services will not be " +                            "performed for account [" + info + "].";                    log.warn(msg, e);                }            }        } else {            if (log.isTraceEnabled()) {                log.trace("This " + getClass().getName() + " instance does not have a " +                        "[" + RememberMeManager.class.getName() + "] instance configured.  RememberMe services " +                        "will not be performed for account [" + info + "].");            }        }    }

getRememberMeManager返回CookieRememberMeManager,onSuccessfulLogin定义在其父类AbstractRememberMeManager中,

    public void onSuccessfulLogin(Subject subject, AuthenticationToken token, AuthenticationInfo info) {        forgetIdentity(subject);        if (isRememberMe(token)) {            rememberIdentity(subject, token, info);        } else {            if (log.isDebugEnabled()) {                log.debug("AuthenticationToken did not indicate RememberMe is requested.  " +                        "RememberMe functionality will not be executed for corresponding account.");            }        }    }

这里就是将一些基本信息设置到Cookie中,不往下看了。

isAuthenticated

登录完成后,如何判断是否登录了呢,简单的调用isAuthenticated就行了。由于登录后,获得的Subject已经不是未登录前的DelegatingSubject,而是WebDelegatingSubject,直接看该函数,

    public boolean isAuthenticated() {        return authenticated;    }

这个authenticated在登录成功后,执行createSubject函数时赋值为true的。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 存钱的收据掉了怎么办 行政拘留法制没有批的怎么办 别人起诉我我该怎么办 去钟落潭看守所送衣服要怎么办 长城宽带账号密码忘了怎么办 预约考试密码忘了怎么办 健康证预约号忘记怎么办啊 人在看守所七个月还没结果怎么办 起诉书和判决书丢了怎么办 进了看守所信用卡逾期怎么办 公安局审讯室监控影相被删除怎么办 关进看守所以前的工作怎么办 上海初中借读生学籍怎么办 外地货北京三环怎么办 谁买了小产权怎么办 狗在小区丢了怎么办 太原回迁房多余的房子怎么办 回迁房被开发商抵押怎么办 回迁房源多开发商扣房怎么办 蝈蝈叫晚上怕吵怎么办 蝈蝈总不停的叫怎么办 按揭房没拿房产证夫妻离婚怎么办 按揭房子房产证还没有到离婚怎么办 结婚7年离婚孩子怎么办 合伙经营KTV股东意见不合怎么办 合伙生意转让意见不合怎么办 租完房子后悔了怎么办 通过中介买房产生纠纷怎么办 天津公租房资格证到期怎么办 买大房子后悔了怎么办 公款私存了该怎么办 外地人怎么办身份证在北京东城区 申请公租房有车怎么办 租了公租房辞职怎么办 申请公租房收入明细没有怎么办 杭州公租房满3年怎么办 小学寄读不转学籍手续怎么办 炸东西的油糊了怎么办 赠送面积为违建怎么办 执法不管违建我怎么办 司法考试毕业院校写错了怎么办