Authenticator的职责是验证用户帐号,是ShiroAPI 中身份验证核心的入口点: [Java] 纯文本查看 复制代码
package org.apache.shiro.authc;
public interface Authenticator {
public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)
throws AuthenticationException;
}
如果验证成功,将返回AuthenticationInfo验证信息,此信息中包含了身份及凭证,如果验证失败将抛出AuthenticationException异常。
SecurityManager接口继承Authenticator,另外还有一个ModularRealmAuthenticator实现,其委托给多个Realm进行验证,验证规则通过AuthenticationStrategy(认证策略)接口指定,默认提供的实现:
FirstSuccessfulStrategy:只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他的忽略。
AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy不同,返回所有Realm身份验证成功的认证信息;
AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,如果有一个失败就失败了。
ModularRealmAuthenticator默认使用AtLeastOneSuccessfulStrategy策略。
简单来说就是: SecurityManager这个核心接口继承了Authenticator,这个认证器使用ModularRealmAuthenticator实现,默认策略为AtLeastOneSuccessfulStrategy。
2、演示AllSuccessfulStrategy策略:
【演示】: 1、新建maven项目,导入依赖 [Java] 纯文本查看 复制代码 <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
2、自定义Realm1、Realm2、Realm3 Realm1验证zhangsan/123,返回凭证zhangsan/123 [Java] 纯文本查看 复制代码 package cn.oriki.shiro.realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.Realm;
public class MyRealm1 implements Realm {
@Override
public String getName() {
return "myRealm1";
}
@Override
public boolean supports(AuthenticationToken token) {
// 仅支持 UsernamePasswordToken 类型的 Token
return token instanceof UsernamePasswordToken;
}
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal(); // 得到用户名
String password = new String((char[]) token.getCredentials()); // 得到密码
if (!"zhangsan".equals(username)) {
throw new UnknownAccountException(); // 如果用户名错误
}
if (!"123".equals(password)) {
throw new IncorrectCredentialsException(); // 如果密码错误
}
// 如果身份认证验证成功,返回一个 AuthenticationInfo 实现;
return new SimpleAuthenticationInfo(username, password, getName());
}
}
Realm2验证lisi/123,返回凭证lisi/123 [Java] 纯文本查看 复制代码 package cn.oriki.shiro.realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.Realm;
public class MyRealm2 implements Realm {
@Override
public String getName() {
return "myRealm2";
}
@Override
public boolean supports(AuthenticationToken token) {
// 仅支持 UsernamePasswordToken 类型的 Token
return token instanceof UsernamePasswordToken;
}
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal(); // 得到用户名
String password = new String((char[]) token.getCredentials()); // 得到密码
if (!"lisi".equals(username)) {
throw new UnknownAccountException(); // 如果用户名错误
}
if (!"123".equals(password)) {
throw new IncorrectCredentialsException(); // 如果密码错误
}
// 如果身份认证验证成功,返回一个 AuthenticationInfo 实现;
return new SimpleAuthenticationInfo(username, password, getName());
}
}
[Java] 纯文本查看 复制代码 package cn.oriki.shiro.realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.Realm;
public class MyRealm3 implements Realm {
@Override
public String getName() {
return "myRealm3";
}
@Override
public boolean supports(AuthenticationToken token) {
// 仅支持 UsernamePasswordToken 类型的 Token
return token instanceof UsernamePasswordToken;
}
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal(); // 得到用户名
String password = new String((char[]) token.getCredentials()); // 得到密码
if (!"zhangsan".equals(username)) {
throw new UnknownAccountException(); // 如果用户名错误
}
if (!"123".equals(password)) {
throw new IncorrectCredentialsException(); // 如果密码错误
}
// 如果身份认证验证成功,返回一个 AuthenticationInfo 实现;
return new SimpleAuthenticationInfo("zhangsan@163.com", password, getName());
}
}
3、配置shiro-authenticator-all-success.ini配置文件 注意:这里只配置Realm1和Realm3 [Java] 纯文本查看 复制代码 #指定 securityManager 的 authenticator 实现
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
securityManager.authenticator=$authenticator
#指定 securityManager.authenticator 的 authenticationStrategy
allSuccessfulStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy
securityManager.authenticator.authenticationStrategy=$allSuccessfulStrategy
myRealm1=cn.oriki.shiro.realm.MyRealm1
myRealm2=cn.oriki.shiro.realm.MyRealm2
myRealm3=cn.oriki.shiro.realm.MyRealm3
securityManager.realms=$myRealm1,$myRealm3
4、测试 [Java] 纯文本查看 复制代码 package cn.oriki.shiro.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Assert;
import org.junit.Test;
public class ShiroTest {
@Test
public void testHelloworld() {
// 1、获取 SecurityManager 工厂,此处使用 Ini 配置文件初始化 SecurityManager
Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory(
"classpath:shiro-authenticator-all-success.ini");
// 2、得到 SecurityManager 实例并绑定给 SecurityUtils
org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// 3、得到 Subject 及创建用户名/密码身份验证 Token(即用户身份/凭证)
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
// 4、登录,即身份验证
try {
subject.login(token);
} catch (AuthenticationException e) {
// 身份验证失败
e.printStackTrace();
}
// 5、断言用户已经登陆
Assert.assertEquals(true, subject.isAuthenticated());
// 获取登陆用户凭证数量
PrincipalCollection principalCollection = subject.getPrincipals();
Assert.assertEquals(2, principalCollection.asList().size());
for (Object object : principalCollection) {
System.out.println(object);
}
// zhangsan
// [email]zhangsan@163.com[/email]
// 6、退出
subject.logout();
}
}
我们也可以自定义认证策略:
[Java] 纯文本查看 复制代码 package org.apache.shiro.authc.pam;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.Realm;
import java.util.Collection;
public interface AuthenticationStrategy {
//在所有 Realm 验证之前调用
AuthenticationInfo beforeAllAttempts(Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException;
//在每个 Realm 之前调用
AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException;
//在每个 Realm 之后调用
AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t)
throws AuthenticationException;
//在所有 Realm 之后调用
AuthenticationInfo afterAllAttempts(AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException;
} 因为每个 AuthenticationStrategy 实例都是无状态的,所有每次都通过接口将相应的认证信息传入下一次流程;通过如上接口可以进行如合并/返回第一个验证成功的认证信息。 自定义实现时一般继承org.apache.shiro.authc.pam.AbstractAuthenticationStrategy即可。
到此基本的身份验证就搞定了,对于AuthenticationToken、AuthenticationInfo和Realm的详细使用后续章节再陆续介绍。
|