黑马程序员技术交流社区

标题: 【上海校区】Shiro学习笔记整理--身份校验之Authenticator(认... [打印本页]

作者: shjava    时间: 2017-10-19 17:12
标题: 【上海校区】Shiro学习笔记整理--身份校验之Authenticator(认...
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());
}
}


Realm3验证zhangsan/123,返回凭证zhangsan@163.com/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 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
// zhangsan@163.com

// 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的详细使用后续章节再陆续介绍。












作者: buguniao    时间: 2017-10-19 18:48
正好学到这一块,希望楼主持续更新呀




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2