shiro源码分析

个人学习路线

Apache Shiro是一个功能强大且灵活的开源安全框架,可以清晰地处理身份验证,授权,企业会话管理和加密。

这是它的官网:

官网
在官网上面有个快速开始,将源码下载下来,
看看shiro的大体架构:http://shiro.apache.org/architecture.html
我就贴一张最重要的图好了,官网介绍的更加清楚.
在这里插入图片描述

跑一遍快速开始:
在这里插入图片描述
这时候对shiro有个简单的了解了…

简单介绍

ini配置文件:

# 用户名,密码,角色
[users]
root = secret, admin
guest = guest, guest

presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz

$ 角色 ,对应的权限
[roles]
admin = *
schwartz = lightsaber:*,aa:*
goodguy = winnebago:drive:eagle5,aa:*

简单看看事例代码:

public class Quickstart {
    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
    public static void main(String[] args) {
        //创建安全中心
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
        //通过提供的类去简化操作
        SecurityUtils.setSecurityManager(securityManager);

        //获取个用户,抽象的用户.
        Subject currentUser = SecurityUtils.getSubject();

        //获取session
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("Retrieved the correct value! [" + value + "]");
        }

        //判断是登录
        if (!currentUser.isAuthenticated()) {
            //用户名密码
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
            token.setRememberMe(true);
            try {
                //登录,没有抛出异常就是认证成功,抛出了就是登录失败了.
                currentUser.login(token);
                //各种异常,顾名思义.账户不存在啊,密码错误啊,用户锁住了等等..
            } catch (UnknownAccountException uae) {
                log.info("There is no user with username of " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
                log.info("Password for account " + token.getPrincipal() + " was incorrect!");
            } catch (LockedAccountException lae) {
                log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                        "Please contact your administrator to unlock it.");
            }
            catch (AuthenticationException ae) {
                //unexpected condition?  error?
            }
        }

        //登录成功,获取用户
        log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

        //是否有指定的角色
        if (currentUser.hasRole("schwartz")) {
            log.info("May the Schwartz be with you!");
        } else {
            log.info("Hello, mere mortal.");
        }

        //是否有指定的权限
        if (currentUser.isPermitted("lightsaber:weild")) {
            log.info("You may use a lightsaber ring.  Use it wisely.");
        } else {
            log.info("Sorry, lightsaber rings are for schwartz masters only.");
        }

      
        //退出
        currentUser.logout();

        System.exit(0);
    }
}

怎么进行认证(登录)的

代码是通过subject调用login方法登录,点到具体实现.
DelegatingSubject这个类实现了Subject,实现了login方法:

    Subject currentUser = SecurityUtils.getSubject();
   currentUser.login(token);

SecurityManager详解

我们看默认的实现DefaultSecurityManager类的uml图,
在这里插入图片描述
DefaultSecurityManager继承了SessionsSecurityManager(可以进行session管理)->AuthorizingSecurityManager(可以进行鉴权)->AuthenticatingSecurityManager(可以进行认证)->RealmSecurityManager(可以进行real管理)->CachingSecurityManager(缓存管理了);这不是很明显的装饰者吗?

CachingSecurityManager

这是CachingSecurityManager有CacheManager接口,我们可以实现自己的缓存管理,可以放在redis里,或者mysql,或者整合ehcache等等.然后可以set,get方便扩展.
在这里插入图片描述

RealmSecurityManager

RealmSecurityManager内部维护了一个Realm集合,也提供了各种set,get,方便我们扩展.
在这里插入图片描述

AuthenticatingSecurityManager

内部维护了一个认证器,当然也可以set,get,默认提供了个简单的认证器ModularRealmAuthenticator
在这里插入图片描述

AuthorizingSecurityManager

内部维护了一个鉴权器,也有默认的,set,get等.
在这里插入图片描述

SessionsSecurityManager

内部维护了一个session管理器
在这里插入图片描述

认证总结

通过一系列的继承,那这个默认的DefaultSecurityManager就可以使用认证啊,鉴权啊,缓存啊,session啊,real管理啊等各种功能啦!!!
登录的话,是认证的功能,
首先通过authenticate方法获取个认证信息authenticate这个方法是认证管理器实现的managerAuthenticatingSecurityManager,使用的认证器去进行认证的,this.authenticator是哪个呢?刚刚说了初始化的时候或设置默认的认证器ModularRealmAuthenticator:
在这里插入图片描述
在这里插入图片描述
然后发现实现里面没有authenticate方法,那就说明,在父类里面,子类里没有的方法就去父类里面找,准没错,父类里的抽象方法,那就去子类里面找实现了
然后父类AbstractAuthenticator里面又是调用的doAuthenticate,这个的实现是在ModularRealmAuthenticator认证器里面,然后就是判断是单realm还是多realm分别进行认证.:
在这里插入图片描述
先看多realm认证的话会判断,是不是要全部通过,才算认证成功,或者是只要一个认证通过,
file
再看单个realm认证doSingleRealmAuthentication方法:就是通过realm的getAuthenticationInfo(AuthenticationToken token)方法 AuthenticatingRealm这个实现的:
file
我们看看doGetAuthenticationInfo的简单实现,一般是我们自己去实现的.:
在这里插入图片描述
在这里插入图片描述
然后就是进行对比了,默认简单的是通过密码进行equals进行对比的…感兴趣可以看看.
当然我们也可以自己实现对比器,CredentialsMatcher这个接口,然后set到我们的管理器里面即可;
对比完成,就是返回认证信息了…
大体流程,我就把网上图贴上来了:

怎么进行鉴权(判断是否有权限)的

鉴权主要就是两个方法,判断是否有对应的角色,判断是否有对应的权限,对应的方法是hasRole跟isPermitted
这两个方法前面讲过,肯定是通过AuthorizingSecurityManager鉴权管理器进行
就看isPermitted方法好了.
代理subject里面实现了isPermitted方法:hasPrincipals方法判断是否登录.是否进行了认证,如果没有进行认证,那判断是否含有权限就没有意义了;
在这里插入图片描述
然后就是AuthorizingSecurityManager里的实现,通过使用鉴权器进行鉴权,同理,肯定有初始化的鉴权器的,同样是ModularRealmAuthorizer;认证的是ModularRealmAuthenticator名字都是modularRealm开头,哈哈!
在这里插入图片描述
在这里插入图片描述
ModularRealmAuthorizer
在这里插入图片描述
这里的reaml我们就看sampleRealm好了,先看uml图:
在这里插入图片描述
AuthorizingRealm->AuthenticatingRealm->CachingReanm想到了啥,是不是跟跟DefaultSecurityManageer有点相似啊,我就不说了,给个眼神自己体会:
最后就是取出所有权限,然后进行对比,如果有就代表有权限.

授权

然后在判断是否含有指定权限之前,肯定要给用户赋予用户该有的权限吧,
在AuthorizingRealm的各种鉴权方法里面都会先获取用户的鉴权信息getAuthorizationInfo返回AuthorizationInfo
在这里插入图片描述
在这里插入图片描述

总结,以前在使用shiro的回想

以前做项目的时候总是听别人说,继承AuthorizingRealm然后实现认证,授权两个方法就可以了,shiro就这两个方法,当时也不知道什么原因,只是会用,现在只有通过源码,流程学习,才明白啊!!!
在这里插入图片描述
以前也是写个类实现UserDao,其实就是各种操作操作数据库的接口,就是在鉴权里面获取用户啊,权限.等各种信息用的.
在这里插入图片描述

世界上所有的不公平都是由于当事人能力不足造成的.
原文地址:https://www.cnblogs.com/javayida/p/13346788.html