摘要:本文通过源码追踪,介绍 Shiro 如何实现简单的登陆控制。本文的源码追踪十分详细,基本所有的调用以及接口对象关系都包含其中。
首先我们看一张图,这张图简要介绍了Shiro登陆说需要的主要组件,大家可以查看官方的登陆步骤参考。

我们接下来更加详细介绍一下在登陆过程中源码的调用流程,前6步为生产环境的代码,也就是我们自己需要实现的最简单的登陆逻辑代码。从第7步开始为 Shiro 源码的调用。
1. SecurityManager 的工厂类
创建一个 SecurityManager 的工厂类,配置权限,这里我们可以使用最简单的 ini 配置文件来导入账号和权限。
1 | Factory<org.apache.shiro.mgt.SecurityManager> factory = |
shiro-realm.ini 文件
1 | [main] |
我们在下面的步骤中我们会详细介绍自定义的 myRealm1。
2. SecurityManager 实例
使用工厂模式获取一个 SecurityManager 的实例。
1 | org.apache.shiro.mgt.SecurityManager securityManager |
3. 配置当前 SecurityManager
使用 SecurityUtils 设置该实例为当前的 SecurityManager。
1 | SecurityUtils.setSecurityManager(securityManager); |
4. 获取 Subject
使用 SecurityUtils 获取当前的 Subject
1 | Subject subject = SecurityUtils.getSubject(); |
5. 准备登陆的数据,
简单的登陆数据为:用户名和密码。
1 | UsernamePasswordToken token = new UsernamePasswordToken("wang", "123"); |
这组数据应该是放到 myRealm1 中进行匹配的。所以后面我们会看到这个用户名和密码在 myRealm1 是如何进行验证的。
6. 开始登陆
使用 Subject 的 login 方法传入准备的登陆数据来进行验证。
1 | try { |
7. Subject 的 login 方法
login 为 Subject 的接口,实际调用的是 DelegationgSubject 的 login 的实现。
1 | public void login(AuthenticationToken token) throws AuthenticationException |
8. SecurityManager 的 login
DelegationSubject 只是一个代理,在 login 方法中调用 securityManager.login(this, token), this 指的是 Subject 对象。
1 | Subject subject = securityManager.login(this, token); |
9. DefaultSecurityManager 的 login
这个 login 还是一个接口,这回是 SecurityManager 的接口,实际调用 DefaultSecurityManager 的 login 实现。
1 | public Subject login(Subject subject, AuthenticationToken token) |
10. login 的具体实现
在该login 实现中,定义了一个 AuthenticationInfo 的 info 对象,这个 info 变量将是我们授权过程重要对象。然后调用父类的 authenticate(token) 方法,该方法为 AuthenticatingSecurityManager 抽象类中的方法,该方法实际是调用了成员变量 private Authenticator authenticator 的 authenticate(token) 方法。该方法是 Authenticator 的接口,这时候调用抽象类 AbstractAuthenticator 的实现。这个 authencitcator 在 AuthenticatingSecurityManager 的构造函数中实际上是实例了 ModularRealmAuthenticator
1 | AuthenticationInfo info; |
11. doAuthenticate 开始授权
上一步的 authenticate 方法实现中,调用了 doAuthenticate(token) 来开始授权。这个 doAuthenticate 实际上是调用的 ModularRealmAuthenticator 的 doAuthenticate。该方法首先获取 realm 的数量,如果是一个 realm 则进入 doSingleRealmAuthentication ,否则进入 doMultiRealmAuthentication。
1 | protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { |
12. Realm
简单案例是单 realm,所以进入 doSingleRealmAuthentication ,函数中调用 AuthenticationInfo info = realm.getAuthenticationInfo(token); 来获取 info。这个 realm 实际上是我们在配置 ini 文件中的 MyRealm1 类。
1 | public class MyRealm1 implements Realm { |
13. Realm 授权逻辑
如果不自己实现 Realm 将会调用了抽象 realm 实现 AuthenticatingRealm 的 getAuthenticationInfo 方法。而这里将会调用我们自定义的 Realm 的 getAuthenticationInfo,自定义getAuthenticationInfo十分简单就是匹配用户名和密码成功就返回SimpleAuthenticationInfo。
这里我们介绍一下抽象的 AuthenticatingRealm 的实现方法,该方法分两步,第一步是拿到 info,第二步是验证 credential 信息。
1 | public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { |
info = doGetAuthenticationInfo(token);这个方法直接把传入的token强制转化为UsernamePasswordToken父类。然后使用getUser(token)获取账号信息SimpleAccount account = getUser(upToken.getUsername());实际上这个User是在配置 realm 的时候能够获取的,可以是最简单的 user/password 配置。assertCredentialsMatch(token, info);这个方法是使用token来匹配info信息,如果没有异常表示匹配成功,如果有异常则表示匹配失败。 该方法是匹配逻辑的关键,首先获取一个matcher:cm,本例最简单的SimpleCredentialsMatcher。 然后cm.doCredentialsMatch(token, info)来进行匹配,该函数就是获取token中的credential,然后获取AuthenticationInfo info的credential然后判断是否相等,具体如何相等,这里就是简单的匹配。
如果匹配成功无异常,返回知道返回 info 为止。
1 | public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { |
至此一个完整的简单的 Shiro 登陆的源码的详细流程就走完了。