本篇博文从SecurityUtils.getSubject().login(token)开始分析,详细解读了shiro如何通过AuthenticationToken找到Realm类完成具体校验逻辑。旨在通过源码分析让同学们轻松发现扩展shiro的的方式,可根据自己的业务需求快速完成自定义AuthenticationToken和自定义Realm构建,了解shiro的部分运行原理。同时对自己学习和运用shiro的经历做个记录和总结。
shiro是啥
根据官网介绍,“Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.”
shiro是一个功能强大,便于使用的java安全框架,可以提供授权、认证、加密和session管理等功能,通过shiro简易的API接口可以简单快速为web应用构建标准化的安全认证体系架构。
shiro中的三个核心模型抽象
shiro中的三个核心概念是:SecurityManager、Subject和Realm。其中SecurityManager是“容器”,所有shiro操作都由SecurityManager管理,subject是主体,是“用户”的抽象。保存有关于主体的详细信息。Realm是“域”,在Realm中进行权限等的操作。
可参考博文和shiro官网。
Token对象保存subject(主体)的身份校验信息,例如:用户名、密码、token字符串等,并提供了principal(例如:用户名,token字符串等)和Credentials(例如:密码)两种抽象类型,Realm相关实现类中通过Token信息完成对subject的安全校验。shiro就是通过token找到对应的realm的。
authentication 认证(login)
authorization 授权(access control)
从SecurityUtils.getSubject().login(token)开始一段源码解读
在web应用中,初始化shiro的相关Bean之后,往往会在filter中进行接口的权限校验。最常使用的是SecurityUtils.getSubject().login(token),进行主体(subject)的合法性判断。shiro会根据Token类型对应的Realm,执行详细的判断逻辑。理解这一过程有利于我们在shiro的基础上进行扩展,定制化满足自身业务需求的realm、filter和AuthenticationToken。
从subject的具体实现类DelegatingSubject的login方法开始,看看shiro是怎样一步一步找到对应的realm的:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190730201507271.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L25ld0NoZW5n,size_16,color_FFFFFF,t_70
SecurityManager接口的相关实现类如上,我们在demo中实例化了DefaultWebSecurityManager bean作为SecurityManager的具体实现。映射到具体realm一定是在securityManager.login(this, token);中进行。我们看下DefaultWebSecurityManager类中login()这个方法:
继续看该类authenticate()方法:
我们看到AnthenticatingSecurityManager类构造方法中实例化了一个ModulerRealmAuthenticator类作为Authenticator接口的实现,所以具体执行的是ModularRealmAuthenticator类的相关方法,但是查看该类并没有提供authenticate()方法。这里shiro通过ModularRealmAuthenticator的父级抽象类实现了一个简单的模板方法模式。子类重写doAuthenticate()方法由抽象类authenticate方法具体执行,看下AbstractAuthenticator具体实现:
看下ModularRealmAuthenticator类的doAuthenticate方法,其中获得所有realm信息的list,对于单条记录执行doSingleRealmAuthentication方法,对于多条记录执行doMultiRealmAuthentication方法。
realm.supports(token);方法决定了token对应要执行的Realm类。可以看到Realm接口定义的support方法的唯一具体实现是在抽象类AuthenticatingRealm中。
getAuthenticationTokenClass()方法获的该类的authenticationTokenClass属性,查看构造方法可以看出该realm默认对应的Token类是UsernamePasswordToken。如下所示:
综上,如果shiro使用DefaultWebSecurityManage这个Bean,默认使用UsernamePasswordToken这个Token,使用AuthenticatingRealm这个realm。
自定义Realm的两种轻松实现方式
通过对shiro小部分源码的分析,我们发现对主体subject进行校验的具体逻辑是在realm中实现的。而完成特定主体与具体realm的对应是通过AuthenticationToken的具体实现类实现的。我们可以定义不同的Token实现类从而解决不同subject与不同realm之间的映射关系,并实现多种逻辑组合。
查看realm源码我们发现shiro提供的多种类型的权限校验抽象,支持redis缓存,jdbc,ldap等多种数据源。
以下提供两种方式:
方式一
在自定义Realm类中构造方法中显示的set特定的AuthenticationToken类,例如我们在自定义的Realm中这样构建:
方式二
继承ModularRealmAuthenticator类重写doAuthenticate方法,例如: