专栏首页波波烤鸭Shiro源码分析之认证过程

Shiro源码分析之认证过程

  在前面分析的基础上我们来继续看下认证的过程


Shiro认证过程的源码

认证过程的时序图

源码跟踪

1.DelegatingSubject类中的login 方法

public void login(AuthenticationToken token) throws AuthenticationException {
	 clearRunAsIdentities();
	 // 进入login
     Subject subject = securityManager.login(this, token);
     // 省略
     ThreadContext.bind(this);
 }

2.DefaultSecurityManager类的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; //propagate
    }
	// 认证成功后创建subject对象
    Subject loggedIn = createSubject(token, info, subject);
	// 绑定对象,创建session
    bind(loggedIn);
	
    onSuccessfulLogin(token, info, loggedIn);
    return loggedIn;
}

3.AuthenticatingSecurityManager类中authenticate方法

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

4.AbstractAuthenticator类中的authenticate方法

public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
	// 省略些不重要的代码
    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) {
            //Exception thrown was not an expected AuthenticationException.  Therefore it is probably a little more
            //severe or unexpected.  So, wrap in an AuthenticationException, log to warn, and propagate:
            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;
}

5.ModularRealmAuthenticator类中的doAuthenticate方法

 protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
     assertRealmsConfigured();
     Collection<Realm> realms = getRealms();
     if (realms.size() == 1) {
     	 // 本案例是单realm所以执行此方法
         return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
     } else {
         return doMultiRealmAuthentication(realms, authenticationToken);
     }
 }

doSingleRealmAuthentication方法

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

6.AuthenticatingRealm类中的getAuthenticationInfo方法

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
	// 账号认证的核心方法
   AuthenticationInfo info = doGetAuthenticationInfo(token);

   if (info == null) {
       if (log.isDebugEnabled()) {
           String msg = "No authentication information found for submitted authentication token [" + token + "].  " +
                   "Returning null.";
           log.debug(msg);
       }
       return null;
   }
	// 获取匹配器
   CredentialsMatcher cm = getCredentialsMatcher();
   if (cm != null) {
   		// 账号验证没有问题就进行秘密验证
       if (!cm.doCredentialsMatch(token, info)) {
           String msg = "The credentials provided for account [" + 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.");
   }

   return info;
}

7.SimpleAccountRealm类中执行doGetAuthenticationInfo方法

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
	 // 获取token中的账号
     UsernamePasswordToken upToken = (UsernamePasswordToken) token;
     // 根据token的账号从IniRealm中获取信息
     SimpleAccount account = 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);
         }

     }
	// 返回账号  账号不存在null 否则账号存在
     return account;
 }

8.CredentialsMatcher中密码验证

public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
    Object tokenCredentials = getCredentials(token);
    Object accountCredentials = getCredentials(info);
    // 验证密码是否匹配
    return equals(tokenCredentials, accountCredentials);
}

至此整个过程结束

总结

  1. 如果没有自定义Realm那么默认的是SimpleAccountRealm
  2. 账号验证和密码验证是分开的,先验证账号如果为null直接抛UnknownAccountException异常,
  3. 如果账号存在则验证密码是否匹配,如果不匹配则会抛IncorrectCredentialsException异常。
  4. 2,3步如果都没有抛异常那么说明认证成功,后续会完成subject的创建及session的设置,以及认证成功后的初始化操作。
  5. 如果我们需要自定义Realm那么继承AuthorizingRealm即可。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Vue教程(动画-半场动画)

      上篇文章我们介绍了过渡动画的实现,包括完整的 入场 和 离场 动画,但是在实际过程中我们有时可能仅仅需要使用半场动画,比如淘宝购物车的下单处理等。

    用户4919348
  • Java项目架构的演变

      大型网站都是从小型网站发展而来的,网站架构也是一样,是从小型网站架构逐步演化而来的,小型网站最开始没有太多人访问,只需要一台服务器就绰绰有余了,这时的架构如...

    用户4919348
  • SpringData【Spring整合Hibernate】

      在resources目录下创建spring的配置文件和数据库连接的属性文件,如下:

    用户4919348
  • ASP.NET Core集成现有系统认证

    我们现在大多数转向ASP.NET Core来使用开发的团队,应该都不是从0开始搭建系统,而是老的业务系统已经在运行,ASP.NET Core用来开发新模块。那么...

    用户1153966
  • 目标检测101:一文带你读懂深度学习框架下的目标检测

    大数据文摘
  • golang beego jwt sso token单点登录尝试-01

    token的使用场景,比如当服务端在多个地方,使用session就需要持久化……。

    hotqin888
  • 系统调用跟踪分析神器--strace

    最近遇到两起应用系统层面性能问题的案例,同事在排查问题的时候使用了strace这款神器,给自己在以后解决系统性能问题时提供了思路,本文学习了解系统分析工具---...

    用户1278550
  • 中科院自动化所提出 BIFT 模型:面向自然语言生成,同步双向推断

    概括地讲,自然语言处理包括两大任务:自然语言文本理解和自然语言文本生成。自然语言文本理解就是让机器洞悉人们所言之意,自然语言文本生成旨在让机器像人一样表达和说话...

    AI科技评论
  • 后验概率(Posterior probability)

    在一个通信系统中,在收到某个消息之后,接收端所了解到的该消息发送的概率称为后验概率。 后验概率的计算要以先验概率为基础。后验概率可以根据通过贝叶斯公式,用先验概...

    easyAI
  • 微信开发获取AccessToken的方式

    对于暂时没有这些权限的微信公众号,开发者可以申请测试账号来体验和测试微信公众平台的所有高级接口功能

    SmileNicky

扫码关注云+社区

领取腾讯云代金券