Shiro__认证过程__源码跟踪分析

shiro认证过程源码

public static void main(String[] args) {
		// 1.获取SecurityManager工厂对象
		Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
		// 2.通过Factory对象获取SecurityManager对象
		SecurityManager securityManager = factory.getInstance();
		// 3.将SecurityManager对象添加到当前运行环境中
		SecurityUtils.setSecurityManager(securityManager);
		// 4.获取Subject对象
		Subject subject = SecurityUtils.getSubject();
		//从客户端获取账号和密码后设置token
		AuthenticationToken token = new UsernamePasswordToken("root", "zsl");
		// 登录操作
		try {
			subject.login(token);
		} catch (UnknownAccountException e) {
			System.out.println("账号出错...");
		} catch(IncorrectCredentialsException e){
			System.out.println("密码出错...");
		}
		// 获取登录的状态
		System.out.println(subject.isAuthenticated());
	
		//必须登入成功后才有授权
		
		// 认证通过后进行权限验证 角色
		//subject.getPrincipal()获取账号
		//subject.hasRole是否存在该角色
		System.out.println(subject.getPrincipal()+"是否具有role1角色:"+subject.hasRole("role1"));
		System.out.println(subject.getPrincipal()+"是否具有role3角色:"+subject.hasRole("role3"));
		//该集合框架中的角色是否全部存在
		boolean[] types = subject.hasRoles(Arrays.asList("role1","role2"));
		System.out.println(subject.getPrincipal()+"是否具有role1和role2角色:"+types[0]+","+types[1]);
		//subject.checkRole("role1");
		//subject.checkRole("role3");
		// 验证权限
		System.out.println(subject.getPrincipal()+"是否具有user:create权限:"+subject.isPermitted("user:create"));
		System.out.println(subject.getPrincipal()+"是否具有user:delete角色:"+subject.isPermitted("user:delete"));
		boolean t = subject.isPermittedAll("user:create","user:delete");
		System.out.println(subject.getPrincipal()+"是否具有user:create和user:delete的权限:"+t);
	
		//检查是否存在该角色和权限,如果不存在则会抛异常
		subject.checkRole("role3");
		subject.checkPermission("aaa");
	}


源码跟踪DelegatingSubject类中login ()

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


源码跟踪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;
}

AuthenticatingSecurityManager类中authenticate方法

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

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


ModularRealmAuthenticator类中的doAuthenticate方法

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

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

assertCredentialsMatch(token, info)

CredentialsMatcher中密码验证

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

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

总结

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

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

偷偷学习被我发现

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值