目录
认证顺序
ModularRealmAuthenticator(认证者)
AuthenticationStrategy(认证策略)
Shiro有3种具体的AuthenticationStrategy实现方式
自定义认证策略
领域认证顺序
隐式顺序
显示指定顺序
示例程序
示例1
示例2
示例3
认证顺序
- 步骤1:应用程序代码调用该Subject.login方法,并传入AuthenticationToken表示最终用户的主体和凭据的构造实例。
- 第2步:Subject实例(通常是一个DelegatingSubject(或子类))通过调用SecurityManager的securityManager.login(token)方法开始实际的身份验证工作。
- 步骤3:SecurityManager作为基本的组件,接收令牌,并通过调用Authenticator来简单地委派给其内部实例authenticator.authenticate(token)。shiro支持我们定义多个Realm,然后通过ModularRealmAuthenticator实例,在身份验证期间协调一个或多个实例。
- 步骤4:如果为该应用程序配置了多个Realm,则shiro的ModularRealmAuthenticator实例将Realm使用其configureed发起多次身份验证尝试AuthenticationStrategy(认证策略)。在Realms调用进行身份验证之前,期间和之后,将调用,AuthenticationStrategy以允许它对每个Realm的结果做出反应。
- 如果仅配置一个Realm,则将直接调用该Realm。不需要调用
AuthenticationStrategy
。(我们的HelloWorld就是单个Realm,我们实际开发中大多数情况下也是单个Realm)
- 第5步:Realm咨询每个配置,以查看是否supports(支持)已提交AuthenticationToken。如果是这样,则支持Realm的getAuthenticationInfo方法将与Submitted一起调用token。该getAuthenticationInfo方法有效地表示针对该特定对象的单个身份验证尝试Realm。(这里Realm的getAuthenticationInfo方法我们先记住,之后的自定义会用到)
ModularRealmAuthenticator(认证者)
在单一领域的应用程序中,ModularRealmAuthenticator
将Realm
直接调用单一领域。如果配置了两个或更多领域,它将使用一个AuthenticationStrategy
实例来协调尝试的发生方式。
shiro默认使用ModularRealmAuthenticator,我们也可以自定义实现。自定义实现后通过shiro.ini直接设置(代码设置方式我想不用多说了把):
[main]
...
authenticator = com.foo.bar.CustomAuthenticator
securityManager.authenticator = $authenticator
AuthenticationStrategy(认证策略)
当为一个应用程序配置两个或多个领域时,ModularRealmAuthenticator依赖于内部AuthenticationStrategy组件来确定认证尝试成功或失败的条件。
AuthenticationStrategy是无状态组件,在尝试进行身份验证时会被查询4次:
- 在任何领域被调用之前
- 在getAuthenticationInfo调用单个Realm方法之前
- 在getAuthenticationInfo调用单个Realm方法之后
- 在所有领域都被调用之后
另外,AuthenticationStrategy负责将每个成功Realm的结果汇总并“捆绑”成一个单一的AuthenticationInfo表示形式。这个最终的聚合AuthenticationInfo实例是该Authenticator实例返回的内容,也是Shiro用来表示Subject的最终身份(又称为Principals)的东西。
Shiro有3种具体的AuthenticationStrategy
实现方式
AuthenticationStrategy策略实现类 | 描述 |
AtLeastOneSuccessfulStrategy(默认) | 如果一个(或多个)领域成功认证,则整个尝试都被视为成功。如果没有成功进行身份验证,则尝试将失败。(有一个成功则成功) |
FirstSuccessfulStrategy | 仅使用从第一个成功通过身份验证的领域返回的信息。所有其他领域将被忽略。如果没有成功通过身份验证,则尝试将失败。(第一个成功即返回) |
AllSuccessfulStrategy | 所有配置的领域都必须成功进行身份验证,才能将整体尝试视为成功。如果任何人未成功通过身份验证,则尝试将失败。(全部成功才成功) |
自定义认证策略
以上3中策略实现已经基本满足了我们的要求,当然我们也可以自定义认证策略。首先继承实现org.apache.shiro.authc.pam.AbstractAuthenticationStrategy类,然后在shiro.int文件中配置:
[main]
authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $authcStrategy
领域认证顺序
隐式顺序
在shiro.ini文件中认证顺序是从上到下
blahRealm = com.company.blah.Realm
...
fooRealm = com.company.foo.Realm
...
barRealm = com.company.another.Realm
显示指定顺序
通过securityManager.realms方法显示的指定顺序(注意:当你使用了显示指定后,比如你定义了3个Realm,但是securityManager.realms指定时只传入了两个,那么只有这两个会生效)
securityManager.realms = $blahRealm, $fooRealm, $barRealm
示例程序
shiro1.ini文件配置
[users]
user1 = password
shiro2.ini文件配置
[users]
user2 = password
示例1
package com.yyoo.mytest.shiro1.demo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import java.util.ArrayList;
import java.util.List;
public class Demo2 {
public static void main(String[] args) {
// 定义多个领域
Realm realm1 = new IniRealm("classpath:shiro1.ini");
Realm realm2 = new IniRealm("classpath:shiro2.ini");
List<Realm> realms = new ArrayList<Realm>(2);
realms.add(realm1);
realms.add(realm2);
SecurityManager securityManager = new DefaultSecurityManager(realms);
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("user2","password");
subject.login(token);
System.out.println(subject.isAuthenticated());
// 没有设定认证策略,将使用默认策略AtLeastOneSuccessfulStrategy,有一个成功即成功,所以user1和user2都能够正常登录
}
}
示例2
package com.yyoo.mytest.shiro1.demo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import java.util.ArrayList;
import java.util.List;
public class Demo3 {
public static void main(String[] args) {
// 定义多个领域
Realm realm1 = new IniRealm("classpath:shiro1.ini");
Realm realm2 = new IniRealm("classpath:shiro2.ini");
List<Realm> realms = new ArrayList<Realm>(2);
realms.add(realm1);
realms.add(realm2);
DefaultSecurityManager securityManager = new DefaultSecurityManager(realms);
SecurityUtils.setSecurityManager(securityManager);
System.out.println(securityManager.getAuthenticator());
ModularRealmAuthenticator authenticator = (ModularRealmAuthenticator)securityManager.getAuthenticator();
// 设置认证策略
authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("user2","password");
subject.login(token);
System.out.println(subject.isAuthenticated());
// 使用策略FirstSuccessfulStrategy,第一个成功就返回(因为两个Realm)
/**
* user1登录,执行了Realm1即停止
* user2登录,执行了Realm1和Realm2,执行Realm2成功即停止
*/
}
}
示例3
package com.yyoo.mytest.shiro1.demo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.pam.AllSuccessfulStrategy;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import java.util.ArrayList;
import java.util.List;
public class Demo4 {
public static void main(String[] args) {
// 定义多个领域
Realm realm1 = new IniRealm("classpath:shiro1.ini");
Realm realm2 = new IniRealm("classpath:shiro2.ini");
List<Realm> realms = new ArrayList<Realm>(2);
realms.add(realm1);
realms.add(realm2);
DefaultSecurityManager securityManager = new DefaultSecurityManager(realms);
SecurityUtils.setSecurityManager(securityManager);
System.out.println(securityManager.getAuthenticator());
ModularRealmAuthenticator authenticator = (ModularRealmAuthenticator)securityManager.getAuthenticator();
// 设置认证策略
authenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("user2","password");
subject.login(token);
System.out.println(subject.isAuthenticated());
// 使用策略AllSuccessfulStrategy,全部成功才算成功
/**
* 由于两个Realm的用户都不一致,所以使用该策略,user1和user2都不能正常登录
*/
}
}