前言
shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证、用户授权。
spring中有spring security (原名Acegi),是一个权限框架,它和spring依赖过于紧密,没有shiro使用简单。
shiro不依赖于spring,shiro不仅可以实现 web应用的权限管理,还可以实现c/s系统,分布式系统权限管理,shiro属于轻量框架,越来越多企业项目开始使用shiro。
Shiro运行流程学习笔记
项目中使用到了shiro,所以对shiro做一些比较深的了解。
也不知从何了解起,先从shiro的运行流程开始。
运行流程
- 首先调用
Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils.setSecurityManager()设置; SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;Authenticator可能会委托给相应的AuthenticationStrategy进行多 Realm 身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多 Realm 身份验证;Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。
绑定线程
这里从看项目源码开始。
看第一步,Subject.login(token)方法。
1 | ini复制代码UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe); |
出现了一个UsernamePasswordToken对象,它在这里会调用它的一个构造函数。
1 | java复制代码public UsernamePasswordToken(final String username, final String password, final boolean rememberMe) { |
据笔者自己了解,这是shiro的一个验证对象,只是用来存储用户名密码,以及一个记住我属性的。
之后会调用shiro的一个工具类得到一个subject对象。
1 | ini复制代码public static Subject getSubject() { |
通过getSubject方法来得到一个Subject对象。
这里不得不提到shiro的内置线程类ThreadContext,通过bind方法会将subject对象绑定在线程上。
1 | scss复制代码public static void bind(Subject subject) { |
1 | scss复制代码public static void put(Object key, Object value) { |
且shiro的key都是遵循一个固定的格式。
1 | arduino复制代码public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY"; |
经过非空判断后会将值以KV的形式put进去。
当你想拿到subject对象时,也可以通过getSubject方法得到subject对象。
在绑定subject对象时,也会将securityManager对象进行一个绑定。
而绑定securityManager对象的地方是在Subject类的一个静态内部类里(可让我好一顿找)。
在getSubject方法中的一句代码调用了内部类的buildSubject方法。
1 | ini复制代码subject = (new Subject.Builder()).buildSubject(); |
PS:此处运用到了建造者设计模式,可以去菜鸟教程仔细了解,
进去观看源码后可以看见。
首先调用无参构造,在无参构造里调用有参构造函数。
1 | csharp复制代码public Builder() { |
在此处绑定了securityManager对象。
当然,他也对securityManager对象的空状况进行了处理,在getSecurityManager方法里。
1 | ini复制代码public static SecurityManager getSecurityManager() throws UnavailableSecurityManagerException { |
真正的核心就在于securityManager这个对象。
SecurityManager
SecurityManager是一个接口,他继承了步骤里所谈到的Authenticator,Authorizer类以及用于Session管理的SessionManager。
1 | java复制代码public interface SecurityManager extends Authenticator, Authorizer, SessionManager { |
看一下它的实现。
且这些类和接口都有依次继承的关系。
Relam
接下来了解一下另一个重要的概念Relam。
Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当与像用户帐户这类安全相关数据进行交互,执行认证(登录)和授权(访问控制)时,Shiro会从应用配置的Realm中查找很多内容。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件 等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
一般情况下,都会自定义Relam来使用。
先看一下实现。
以及自定义的一个UserRelam。
看一下类图。
每个抽象类继承后所需要实现的方法都不一样。
1 | scala复制代码public class UserRealm extends AuthorizingRealm |
这里继承AuthorizingRealm,需要实现它的两个方法。
1 | java复制代码//给登录用户授权 |
之后再来看看这个验证方法,在之前的步骤里提到了,验证用到了Authenticator ,也就是第五步。
Authenticator
Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。
再回到之前登录方法上来看看。
subject.login(token)在第一步中调用了Subject的login方法,找到它的最终实现DelegatingSubject类。
里面有调用了securityManager的login方法,而最终实现就在DefaultSecurityManager这个类里。
1 | ini复制代码Subject subject = securityManager.login(this, token); |
1 | java复制代码public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException { |
之后就是验证流程,这里我们会看到第四步,点进去会到抽象类AuthenticatingSecurityManager。再看看它的仔细调用。
1 | java复制代码public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException { |
真正的调用Relam进行验证并不在这,而是在ModularRealmAuthenticator。
他们之间是一个从左到右的过程。
1 | scss复制代码protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { |
在这里咱们就看这个doSingleRealmAuthentication方法。
单Relam验证。
1 | java复制代码protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) { |
再看看多Relam的。
1 | ini复制代码protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) { |
会发现调用的都是Relam的getAuthenticationInfo方法。
看到了熟悉的UserRelam,此致,闭环了。
但是也只是了解了大概的流程,对每个类的具体作用并不是很了解,所以笔者还是有很多地方要去学习,不,应该说我本来就是菜鸡,就要学才能变带佬。
最后
大家看完有什么不懂的可以在下方留言讨论,也可以关注我私信问我,我看到后都会回答的。也欢迎大家关注我的公众号:前程有光,马上金九银十跳槽面试季,整理了1000多道将近500多页pdf文档的Java面试题资料放在里面,助你圆梦BAT!文章都会在里面更新,整理的资料也会放在里面。谢谢你的观看,觉得文章对你有帮助的话记得关注我点个赞支持一下!
本文转载自: 掘金