Shiro源码剖析

在原作者基础上,针对部分概念做了补充,觉得不错可以给原文博主点个赞,原文:https://blog.csdn.net/caoyang0105/article/details/82769293 

先粘出登录的代码

@RequestMapping(value="/submitLogin",method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> submitLogin(String username,String password){
        System.out.println(username+"-----"+password);
        String msg = "";
        Map<String, Object> map = new HashMap<>();
        try {
            UsernamePasswordToken token = token = new UsernamePasswordToken(username, password);
            char[] password1 = token.getPassword();
            String s = password1.toString();
 
            System.out.println("*******"+s);
            SecurityUtils.getSubject().login(token);
            token.setRememberMe(true);
            map.put("status",200);
        } catch (UnknownAccountException e) {
            msg = "UnknownAccountException -- > 账号不存在:";
            map.put("status",400);
        } catch (IncorrectCredentialsException e){
            msg = "IncorrectCredentialsException -- > 密码不正确:";
            map.put("status",500);
        }catch (Exception exception){
            msg = "else >> "+exception;
            System.out.println("else -- >" + exception);
        }
        return map;
    }


可以看到已经获取到了username和password ,为了接下来的认证过程,我们需要获取subject对象,也就是代表当前登录用户,并且要将username和password两个变量设置到UsernamePasswordToken对象的token中, 调用SecurityUtils.getSubject().login(token)方法,将 token传入

接下来看看login方法的实现:

主要还是用到了securityManager安全管理器

进入securityManager里边的login方法,看看他的实现:

 在这个方法中定义了AuthenticationInfo对象来接受从Realm传来的认证信息

进入authenticate方法中

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

发现调用了authenticator的authenticate这个方法

进入this.authenticator.authenticate(token)这个方法中

在这个 类中调用了这个方法,再进去看他的实现

protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        this.assertRealmsConfigured();
        Collection<Realm> realms = this.getRealms();
        return realms.size() == 1?this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken):this.doMultiRealmAuthentication(realms, authenticationToken);
    }


在这里才是刚才前边的那个authenticator的实现, this.assertRealmsConfigured() 这个方法是判断realm是否存在,不存在则抛出异常,他会根据realm的个数来判断执行哪个方法,上篇中springboot整合shiro我只配置了一个realm,所以他只会执行this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken)这个方法,并且会将 realm和token作为参数传入,这里的realm其实就是自己定义的MyShiroRealm

接下来再进入doSingleRealmAuthentication这个方法中,

在这里 他会先判断realm是否支持token

接下来执行else中的getAuthenticationInfo方法

this.getCachedAuthenticationInfo(token)这个方法是从shiro缓存中读取用户信息,如果没有,才从realm中获取信息。如果是第一次登陆,缓存中肯定没有认证信息,所以会执行this.doGetAuthenticationInfo(token)这个方法。
查看this.doGetAuthenticationInfo(token)方法,会发现有这么几个类提供我们选择

其中就有我们自定义的realm,进去

 所以 ,上边的doGetAuthorizationInfo是 执行的我们自定义realm中重写的doGetAuthorizationInfo这个方法。这个方法就会从数据库中读取我们所需要的信息,最后封装成SimpleAuthorizationInfo返回去。

这两个方法虽然名字很像,但是意义是不一样的,doGetAuthorizationInfo方法是进行权限验证,doGetAuthenticationInfo是进行身份验证的(登录验证),相信很多初学者对于这两个方法的调用时机可能不太明白,今天楼主搞了一下午的测试大致明白这两个方法的调用时机。

1.doGetAuthorizationInfo方法
    该方法主要是用于当前登录用户授权(作者小白插一句:我实现该方法时只是进行了当前登录用户的角色与权限初始化,至于授权判断估计shiro帮我们做了)

    1.调用SecurityUtils.getSubject().isPermitted(String str)方法时会调用doGetAuthorizationInfo方法,SecurityUtils.getSubject().isPermitted(String str)方法返回的是boolean值所以开发者得自定义业务逻辑。

    2.在配置文件中配置有类似/**=roles["user"]或者/**=perms[“user”](/**路径是开发者自行定义)的配置的时候会调用doGetAuthorizationInfo方法,这个时候有两种情况:

       一、 当当前没有用户登录时shiro会帮我们跳转到login.jsp(默认会找根目录下的login.jsp)

       二、当前有登录用户时shiro会自动判定当前登录用户的角色或者权限符合访问当前访问路径,如果不符合那么将跳转到开  

            发者所配置的未授权提示页面( <property name="unauthorizedUrl" value="/unauthorized.jsp"/>),这种调用情

            下doGetAuthorizationInfo方法只会被调用一次,除非更换了当前登录用户。

2.doGetAuthenticationInfo方法
    该方法是进行用户验证的
    1.调用currUser.login(token)方法时会调用doGetAuthenticationInfo方法,作者小白重写该方法的主要内容是根据提交的用户名与密码到数据库进行匹配,如果匹配到了就返回一个AuthenticationInfo对象否则返回null,同样shiro会帮我们进行判断当返回null的时候会抛出一个异常,开发者可根据该异常进行相应的逻辑处理

现在获取到认证信息了,接下来就是shiro怎么去进行认证,我们返回去看

 获取 完信息之后就是进行密码匹配,进入assertCredentialsMatch方法中看一下,

首先 获取一个CredentialsMatcher对象,译为凭证匹配器,这个类的主要作用就是将用户输入的密码一某种计算加密。

再看一下cm.doCredentialsMatch(token,info)

这里会用到equals方法对token中加密的密码和从数据库中取出来的info中的密码进行对比,如果认证相同就返回true,失败就返回false,并抛出AuthenticationException,将info返回到defaultSecurityManager中,到此认证过程结束。
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值