shiro login

1调用subject的login方法,suject去掉用DelegatingSubject实现类中的login(token)

shiro login

clearRunAsIdentities

这个方法是用来清除没用的session,以下为详细实现

private void clearRunAsIdentities() {
    Session session = this.getSession(false);//查询session如果有则返回没有则null  如果参数为true则是没有就创建一个新的session
    if (session != null) {
        session.removeAttribute(RUN_AS_PRINCIPALS_SESSION_KEY);//清除特定的session
    }

}

这里就不得不提下

RUN_AS_PRINCIPALS_SESSION_KEY

这是DelegatingSubject中的静态常量用来存储登录用户的名字

shiro login

再回到DelegatingSubject中的login发现它调用的是(侧面验证了Subject主体的所有请求都经过安全管理器的分发)

securityManager中的login(this//DelegatingSubject,token记录用户账号密码的验证信息)

进入这个login

public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
    AuthenticationInfo info;//声明的一个认证信息的对象
    try {
        info = this.authenticate(token);
    } catch (AuthenticationException var7) {
        AuthenticationException ae = var7;

        try {
            this.onFailedLogin(token, ae, subject);
        } catch (Exception var6) {
            if (log.isInfoEnabled()) {
                log.info("onFailedLogin method threw an exception.  Logging and propagating original AuthenticationException.", var6);
            }
        }

        throw var7;
    }

 Subject loggedIn = this.createSubject(token, info, subject);//当你登录成功 会根据你主体 验证信息创建一个新的主题对象
    this.onSuccessfulLogin(token, info, loggedIn);
//当你登录成功  内部会调用RememberMeManager记住你的登录主体对象  相当于id
return loggedIn;}
进入
  info = this.authenticate(token);
这是它父类中的一个方法:如下图
shiro login

public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
    return this.authenticator.authenticate(token);//找到了认证器组件 调用认证器的认证方法
}    

进入了一个新的类

shiro login

public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
    if (token == null) {
        throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");
    } else {
        log.trace("Authentication attempt received for token [{}]", token);

        AuthenticationInfo info;
        try {
            info = this.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 var8) {
            AuthenticationException ae = null;
            if (var8 instanceof AuthenticationException) {
                ae = (AuthenticationException)var8;
            }

            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, var8);
                if (log.isWarnEnabled()) {
                    log.warn(msg, var8);
                }
            }

            try {
                this.notifyFailure(token, ae);
            } catch (Throwable var7) {
                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, var7);
                }
            }

            throw ae;
        }

        log.debug("Authentication successful for token [{}].  Returned account [{}]", token, info);
        this.notifySuccess(token, info);//提示登录成功  
        return info;
    }
}

继续追踪

 info = this.doAuthenticate(token);//验证器进行验证

shiro login

进入抽象类的子类

protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
    this.assertRealmsConfigured();//判断数据源的配置   这是本类中的方法如下段代码判断是否会出现空的数据源
    Collection<Realm> realms = this.getRealms();//发现一个基于ini的数据源  如下下图
    return realms.size() == 1 //根据判断做单个还是多个数据源的数据验证 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next()//数据源(迭代遍历数据库中的信息), authenticationToken//用户输入信息) : this.doMultiRealmAuthentication(realms, authenticationToken);
}
protected void assertRealmsConfigured() throws IllegalStateException {
    Collection<Realm> realms = this.getRealms();
    if (CollectionUtils.isEmpty(realms)) {
        String msg = "Configuration error:  No realms have been configured!  One or more realms must be present to execute an authentication attempt.";
        throw new IllegalStateException(msg);
    }
}

shiro login

如上图里面使用LinkedHashMap存储了两个用户

   进入单个数据源验证

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);
    } else {
        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);
        } else {
            return info;
        }
    }
}

判断是否支持令牌的代码(你可以发现其实就是一个token的格式验证)

return token != null && this.getAuthenticationTokenClass().isAssignableFrom(token.getClass());

其中isAssignableFrom(class)是用来判断调用该方法的接口的类型是否跟参数中的一致

追踪realm.getAuthenticationInfo(token)
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    AuthenticationInfo info = this.getCachedAuthenticationInfo(token);//获取缓存中的认证信息
    if (info == null) {
        info = this.doGetAuthenticationInfo(token);
        log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
        if (token != null && info != null) {//查找到的数据库信息info不为空  并且用户提交的验证信息不为空
            this.cacheAuthenticationInfoIfPossible(token, info);//缓存验证信息
        }
    } else {
        log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
    }

    if (info != null) {
        this.assertCredentialsMatch(token, info);//判断凭证信息是否匹配
    } else {
        log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
    }

    return info;
}

追踪

.getCachedAuthenticationInfo(token)
private AuthenticationInfo getCachedAuthenticationInfo(AuthenticationToken token) {
    AuthenticationInfo info = null;
    Cache<Object, AuthenticationInfo> cache = this.getAvailableAuthenticationCache();//获取存货的验证缓存
    if (cache != null && token != null) {
        log.trace("Attempting to retrieve the AuthenticationInfo from cache.");
        Object key = this.getAuthenticationCacheKey(token);//这个方法 内部如下一行:
        return token != null ? token.getPrincipal() : null; 
Principal        

1)可以是uuid 
2)数据库中的主键 
3)LDAP UUID或静态DN 
4)在所有用户帐户中唯一的字符串用户名。

也就是说这个值必须是唯一的。也可以是邮箱、身份证等值。也就是用户上传的验证的唯一标示

info = (AuthenticationInfo)cache.get(key); //获取缓存中以这个上传标识符为key 的已经查找过的数据 if (info == null) { log.trace("No AuthorizationInfo found in cache for key [{}]", key); } else { log.trace("Found cached AuthorizationInfo for key [{}]", key); } } return info;}追踪对令牌的验证
info = this.doGetAuthenticationInfo(token);

进入SimpleAccountRealm

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    UsernamePasswordToken upToken = (UsernamePasswordToken)token;//将token强制转换为实际类型
    SimpleAccount account = this.getUser(upToken.getUsername());//获取其中的用户
    if (account != null) {
        if (account.isLocked()) {//判断账户是否被锁定
            throw new LockedAccountException("Account [" + account + "] is locked.");
        }

        if (account.isCredentialsExpired()) {//判断是否失效
            String msg = "The credentials for account [" + account + "] are expired";
            throw new ExpiredCredentialsException(msg);
        }
    }

    return account;
}
追踪
this.getUser(upToken.getUsername());
发现是一个本类中的方法    使用读写锁
protected SimpleAccount getUser(String username) {
    this.USERS_LOCK.readLock().lock();

    SimpleAccount var2;
    try {
        var2 = (SimpleAccount)this.users.get(username);//根据用户名查找用户信息
    } finally {
        this.USERS_LOCK.readLock().unlock();
    }

    return var2;
}

使用了一个读写锁

追踪

this.cacheAuthenticationInfoIfPossible(token, info);

private void cacheAuthenticationInfoIfPossible(AuthenticationToken token, AuthenticationInfo info) {
    if (!this.isAuthenticationCachingEnabled(token, info)) {//判断认证缓存是否可用
        log.debug("AuthenticationInfo caching is disabled for info [{}].  Submitted token: [{}].", info, token);
    } else {//可用则进行缓存
        Cache<Object, AuthenticationInfo> cache = this.getAvailableAuthenticationCache();
        if (cache != null) {
            Object key = this.getAuthenticationCacheKey(token);
            cache.put(key, info);
            log.trace("Cached AuthenticationInfo for continued authentication.  key=[{}], value=[{}].", key, info);
        }

    }
}
追踪
this.assertCredentialsMatch(token, info);//判断凭证信息是否匹配
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
    CredentialsMatcher cm = this.getCredentialsMatcher();//获取凭证匹配器  这个匹配器如下图
    if (cm != null) {
        if (!cm.doCredentialsMatch(token, info)) {//该方法内部通过equal验证token信息和info密码是否一致  下面的异常就是密码错误时的异常
            String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
            throw new IncorrectCredentialsException(msg);
        }
    } else {
        throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify credentials during authentication.  If you do not wish for credentials to be examined, you can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
    }
}

shiro login

如上图所示这该类的一个属性 通过构造方法附一个simple验证器