
不解释,全网最全Shiro认证与授权原理分析
本篇为《Shiro从入门到精通》系列第二篇,在上篇《还在手写filter进行权限校验?尝试一下Shiro吧》中,我们学习了Shiro的基本功能、架构以及各个组件的概念。本篇文章继续深入,以官方示例为基础,讲解使用Shiro的流程以及认证和授权的原理分析。下面开始正文:
前言
Shiro作为常用的权限框架,可被用于解决认证、授权、加密、会话管理等场景。Shiro对其API进行了友好的封装,如果单纯的使用Shiro框架非常简单。
但如果使用了多年Shiro,还依旧停留在基本的使用上,那么这篇文章就值得你学习一下。只有了解Shiro的底层和实现,才能够更好的使用和借鉴,同时也能够避免不必要的坑。
下面以官方提供的实例为基础,讲解分析Shiro的基本使用流程,同时针对认证和授权流程进行更底层的原理讲解,让大家真正了解我们所使用的Shiro框架,底层是怎么运作的。
Shiro组成及框架
在学习Shiro各个功能模块之前,需要先从整体上了解Shiro的整体架构,以及核心组件所处的位置。下面为官方提供的架构图:
shiro
上图可以看出Security Manager是Shiro的核心,无论认证、授权、会话管理等都是通过它来进行管理的。在使用和分析原理之前,先来了解后面会用到的组件及其功能:
- Subject:主体,可以是用户或程序,主体可以访问Security Manager以获得认证、授权、会话等服务;
- Security Manager:安全管理器,主体所需的认证、授权功能都是在这里进行的,是Shiro的核心;
- Authenticator:认证器,主体的认证过程通过Authenticator进行;
- Authorizer:授权器,主体的授权过程通过Authorizer进行;
- Session Manager:shiro的会话管理器,与web应用提供的Session管理分隔开;
- Realm:域,可以有一个或多个域,可通过Realm存储授权和认证的逻辑;
上面只列出了部分组件及功能,其他更多组件在后续文章会逐步为大家实践讲解。了解了这些组件和核心功能之后,下面以官方的示例进行讲解。
官方实例分析
Shiro官方示例地址为:http://shiro.apache.org/tutorial.html ,需要留意的是官方示例已经有些老了,在实践中会做一些调整。
我们先在本地将环境搭建起来,运行程序。创建一个基于Maven的Java项目,引入如下依赖:
目前最新版本为1.7.0,可根据需要引入其他版本。运行官方实例比较坑的地方是,还需要引入log4j和slf4j对应的依赖。都是Apache的项目,因此底层默认采用了log4j的日志框架,如果不引入对应的日志依赖,会报错或无法打印日志。
紧接着,在resources目录下创建一个shiro.ini文件,将官网提供内容配置内容复制进去:
这个文件可看成是一个Realm,其实就是shiro默认的IniRealm,当然在不同的项目中用户、权限、角色等信息可以以各种形式存储,比如数据库存储、缓存存储等。
上述配置文件格式的语义也比较明确,配置了用户和角色等信息,大家留意看一下注释中对数据格式的解释。root = secret, admin表示用户名root,密码是secret,角色是admin。其中角色可以配置多个,在后面依次用逗号分隔即可。schwartz = lightsaber:*表示角色schwartz拥有权限lightsaber:*。
继续创建一个Tutorial类,将官网提供的代码复制进去,由于采用的是1.7.0版本,官网实例中下面的代码已经没办法正常运行了:
原因是IniSecurityManagerFactory类已经被标注废弃了,替代它的是Environment接口及其实现类。因此需将上述获取SecurityManager的方式改为通过shiro提供的Environment来初始化和获取:
改造之后的完整代码如下(其中英文注释已翻译成中文注释):
完整项目源码:https://github.com/secbr/shiro/tree/main/shiro-demo
执行程序,打印日志信息如下,可以看到每一步的执行输出:
上述代码中包含了11个主要的流程:
- 1、初始化环境,这里主要是加载shiro.ini配置文件的信息;
- 2、获取SecurityManager安全管理器;
- 3、获取当前主体(用户);
- 4、获取当前主体的会话;
- 5、向会话中存储一些内容(不需要web容器或EJB容器);
- 6、再次从会话中获取存储的内容,并比较与存储值是否一致;
- 7、判断当前用户是否认证;
- 8、将账号和密码封装到UsernamePasswordToken中;
- 9、开启记住我;
- 10、检查是否有指定角色权限;
- 11、退出登录。
下面我们对几个核心步骤步骤进行分析说明。
初始化环境
源码中通过Environment对象来加载配置文件和初始化SecurityManager,然后通过工具类SecurityUtils对SecurityManager进行设置。
在实践中,可根据具体情况进行初始化,比如实例中通过Environment加载文件,也可以直接创建DefaultSecurityManager,在web项目采用DefaultWebSecurityManager等。
这里的配置文件相当于一个Realm,部分SecurityManager实现类(比如:DefaultSecurityManager)提供了setRealm方法,用户可通过该方法自定义设置Realm。
总之,无论获取SecurityManager的方式如何,都需要有这么一个SecurityManager用来处理后续的认证、授权等处理,可见SecurityManager的核心地位。
认证流程
在上述实例代码中,先将认证功能相关的核心代码抽离出来,包含以下代码及操作步骤(省略了SecurityManager的创建和设置):
从这个代码流程上来看,Shiro的认证过程包括:初始化环境,获取当前用户主体,判断是否认证过,将账号密码进行封装,进行认证,认证完成校验权限。可以通过下图来表示整个流程。
shiro
接下来,通过跟踪源码,来看看Shiro的认证流程涉及到哪些组件。
认证原理分析
认证的入口程序是login方法,以此方法为入口,进行跟踪,并忽略掉非核心操作,可得出认证逻辑经过以下代码执行步骤:
上述代码包括了认证过程中一些核心流程,抽离出核心部分,整理成流程图如下:
shiro
可以看出,整个认证过程中涉及到了SecurityManager、Subject、Authenticator、Realm等组件,相关组件的功能可参考架构图中的功能说明。
授权原理
实例中授权调用的代码比较少,主要就是以下几个方法:
下面以hasRole方法为例,进行追踪分析源代码,看看具体的实现原理。
上述代码包括了授权过程中一些核心流程,抽离出核心部分,整理成流程图(isPermitted方法类似,读者可自行追踪),如下:
可以看出,整个认证过程中涉及到了SecurityManager、Subject、Authorizer、Realm等组件,相关组件的功能可参考架构图中的功能说明。
自定义Realm
通过上面认证和授权流程及原理的分析,会发现无论哪个操作都需要通过Realm来定义用户认证时需要的账户信息和授权时的权限信息。但一般情况下不会使用官网示例的基于“ini配置文件”的方式,而是通过自定义Realm组件来实现。
以下面的示例来说,我们可以使用Shiro内置的Realm组件:
上述示例中创建了一个SimpleAccountRealm对象,并把初始化的账户信息通过addAccount方法添加进去。
实践中自定义Realm的方法通常是继承AuthorizingRealm类,并实现其doGetAuthorizationInfo方法和doGetAuthenticationInfo方法。在上面的流程梳理过程中,我们已经知道doGetAuthorizationInfo方法为授权功能的实现,而doGetAuthenticationInfo方法为认证的功能实现。关于具体实例,后续会用专门的实例来讲解。
小结
本篇文章从Shiro的整体架构、使用实例,再到认证和授权的源码分析,想必经过这番学习,大多数朋友已经了解了使用多年的Shiro框架到底是怎么运作的了。当了解了这些底层的实现,再回头看,是不是感觉之前有疑惑的地方豁然开朗了?是不是有一种原来如此的感觉?那么,恭喜你,你学到了。
文章转载自公众号: 程序新视界
