Shiro认证流程源码分析

创建认证测试类

创建TestAuthenticator类,代码如下:

public class TestAuthenticator {

    public static void main(String[] args) {
        //1.创建安全管理器对象
        DefaultSecurityManager securityManager = new DefaultSecurityManager();

        //2.给安全管理器设置realm
        securityManager.setRealm(new IniRealm("classpath:shiro.ini"));

        //3.SecurityUtils 给全局安全工具类设置安全管理器
        SecurityUtils.setSecurityManager(securityManager);

        //4.关键对象 subject 主体
        Subject subject = SecurityUtils.getSubject();

        //5.创建令牌
        UsernamePasswordToken token = new UsernamePasswordToken("heling", "123");

        try{
            System.out.println("认证状态:" + subject.isAuthenticated());
            subject.login(token); //用户认证
            System.out.println("认证状态:" + subject.isAuthenticated());
        }catch (UnknownAccountException e){
            e.printStackTrace();
            System.out.println("认证失败:用户名不存在");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("认证失败:密码错误");
        }
    }
}

shiro.ini内容为:

[users]
heling=123

运行测试结果为:

认证状态:false
认证状态:true

以上过程是shiro中最简单的一个认证,其中的权限管理数据是通过IniRealm的形式去读取配置文件shiro.ini来加载数据,但是以后的权限管理数据肯定都是来源于数据库的,所以我们要把数据更改到数据库。
Shiro从Realm获取安全数据 (如用户,角色,权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较,来确定用户的身份是否合法;也需要从Realm得到用户相应的角色、权限,进行验证用户的操作是否能够进行,可以把RealmDataSource;Realm可以决定我们的权限管理数据是来源于ini配置文件还是数据库,也就是数据的调配是由Realm来执行的,为了搞清楚Realm的调配流程,我们简单看一下认证的源码。

认证源码分析

用户名认证

首先在subject.login(token);处打上断点,然后Debug运行。
在这里插入图片描述
程序停在断点之后,我们进行login方法。
在这里插入图片描述
可以看到虽然是subject调用的login,但是真正去执行的是DelegatingSubject中的login,方法刚开始调用了一个clear方法,然后由安全管理器进行认证,内部的this就是DelegatingSubject类,token就是我们传过来的由身份信息和凭证信息组成的令牌。然后再进入securityManagerlogin方法。
在这里插入图片描述我们发现实际上真正去执行的是DefaultSecurityManager中的login方法,方法刚开始就执行一个authenticate方法,传入token,然后继续进入这个方法内部。
在这里插入图片描述
继续调用AuthenticatingSecurityManager类中的authenticate方法,在这个方法中又调用了当前类中的authenticator对象的authenticate方法,继续进入这个方法。
在这里插入图片描述
发现调用了AbstractAuthenticator中的authenticate方法,在这个方法中由调用了doAuthenticate方法,继续进入doAuthenticate
在这里插入图片描述
来到了ModularRealmAuthenticator类中的doAuthenticate方法,方法开始时有一个断言询问是否配置Realm,然后getRealms方法拿到了所有的域,我们只有一个域,所以realms.size() == 1,进入doSingleRealmAuthentication方法。然后进入doSingleRealmAuthentication方法查看。

在这里插入图片描述
doSingleRealmAuthentication方法刚开始询问realm是否支持token,realm肯定支持,所以realm调用getAuthenticationInfo方法。
在这里插入图片描述
可以发现它是从缓存中拿取认证信息,因为程序第一次运行是没有缓存的,所以这次肯定拿不到信息。
在这里插入图片描述
继续调用doGetAuthenticationInfo方法。
在这里插入图片描述
这里可以看到doGetAuthenticationInfo方法将token取出来,然后强转成UsernamePasswordToken,然后通过upToken.getUsername()获得用户名,最后使用getUser方法拿到用户。我们继续进入getUsername方法。
在这里插入图片描述
可以看到已经拿到用户名heling。然后下一步回去。
在这里插入图片描述
再进入getUser方法。
在这里插入图片描述username被传进来了,往下走,发现this.users的size=1,因为我们的配置文件shiro.ini中只有一个用户。然后这里根据用户名拿到用户,返回SimpleAccount,继续下一步。
在这里插入图片描述
这时已经拿到account,所以account肯定不为空,而且account也没有上锁,也没有做过验证密码是否过期的处理,所以最终在SimpleAccountRealm类中通过doGetAuthenticationInfo方法完成了一个用户名的认证。

密码认证

return acount之后继续下一步,回到了AuthenticatingRealm类,
在这里插入图片描述这里拿到的info肯定不为空了,继续下一步,因为token != null && info != null,所以增加了缓存(this.cacheAuthenticationInfoIfPossible(token, info);)。继续往下走。
在这里插入图片描述
如果info不为空的话,就进入一个断言,判断token中的密码和info中的密码是否相等。进入assertCredentialsMatch方法查看。
在这里插入图片描述
进入方法,拿到了一个密码匹配器,如果密码匹配器不为空的话,就调用doCredentialsMatch方法对token中的密码和info中的密码进行比对,如果密码不正确就抛出IncorrectCredentialsException异常。所以最终密码的校验是在AuthenticatingRealm类中的assertCredentialsMatch方法中完成的。
如果以后我们要把数据换成数据库的实现,那么我们只需把SimpleAccountRealm类中doGetAuthenticationInfo方法的实现换成读取数据库,因为SimpleAccountRealm继承自AuthorizingRealm类,所以如果我们要自定义Realm,我们也应该自己写一个类去继承AuthorizingRealm并重写doGetAuthenticationInfo方法。密码校验是不需要我们自己处理的,因为我们在用户名认证之后返回info信息,AuthenticatingRealm类中的assertCredentialsMatch方法会自动帮我们校验。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值