前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Shiro源码分析之认证过程

Shiro源码分析之认证过程

作者头像
用户4919348
发布2019-04-02 11:08:38
1.1K0
发布2019-04-02 11:08:38
举报
文章被收录于专栏:波波烤鸭波波烤鸭

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


Shiro认证过程的源码

认证过程的时序图

在这里插入图片描述
在这里插入图片描述

源码跟踪

1.DelegatingSubject类中的login 方法

代码语言:javascript
复制
public void login(AuthenticationToken token) throws AuthenticationException {
	 clearRunAsIdentities();
	 // 进入login
     Subject subject = securityManager.login(this, token);
     // 省略
     ThreadContext.bind(this);
 }

2.DefaultSecurityManager类的login方法

代码语言:javascript
复制
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方法

代码语言:javascript
复制
public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
    return this.authenticator.authenticate(token);
}

4.AbstractAuthenticator类中的authenticate方法

代码语言:javascript
复制
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方法

代码语言:javascript
复制
 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方法

代码语言:javascript
复制
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方法

代码语言:javascript
复制
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方法

代码语言:javascript
复制
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中密码验证

代码语言:javascript
复制
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即可。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年03月09日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Shiro认证过程的源码
    • 认证过程的时序图
      • 源码跟踪
      • 总结
      相关产品与服务
      访问管理
      访问管理(Cloud Access Management,CAM)可以帮助您安全、便捷地管理对腾讯云服务和资源的访问。您可以使用CAM创建子用户、用户组和角色,并通过策略控制其访问范围。CAM支持用户和角色SSO能力,您可以根据具体管理场景针对性设置企业内用户和腾讯云的互通能力。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档