在流程图3中,有Authenticator和AuthenticationStrategy2个接口。

Authenticator的职责是验证用户帐号,是Shiro API中身份验证核心的入口点:

它只有一个方法:

public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)throws AuthenticationException;

如果验证成功,将返回 AuthenticationInfo 验证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的 AuthenticationException 实现。

选中AuthenticationInfo,按住Ctrl+t,可以看到它的继承体系。

SecurityManager接口继承了Authenticator,另外还有一个ModularRealmAuthenticator实现,其委托给多个Realm进行验证,验证规则通过AuthenticationStrategy接口指定,默认提供的实现:

FirstSuccessfulStrategy:只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他的忽略;

AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy不同,返回所有Realm身份验证成功的认证信息;

AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,如果有一个失败就失败了。

ModularRealmAuthenticator默认使用AtLeastOneSuccessfulStrategy策略。

一个例子:

假设我们有三个realm:

myRealm1: 用户名/密码为zhang/123时成功,且返回身份/凭据为zhang/123;

myRealm2: 用户名/密码为wang/123时成功,且返回身份/凭据为wang/123;

myRealm3: 用户名/密码为zhang/123时成功,且返回身份/凭据为zhang@163.com/123,和myRealm1不同的是返回时的身份变了;

三个reaml文件如下:

package com.lgy.shiro;
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 {public String getName() {return "myrealm1";}public boolean supports(AuthenticationToken token) {return token instanceof UsernamePasswordToken; //仅支持UsernamePasswordToken类型的Token}public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String username = (String)token.getPrincipal();  //得到用户名String password = new String((char[])token.getCredentials()); //得到密码if(!"zhang".equals(username)) {throw new UnknownAccountException(); //如果用户名错误}if(!"123".equals(password)) {throw new IncorrectCredentialsException(); //如果密码错误}//如果身份认证验证成功,返回一个AuthenticationInfo实现;return new SimpleAuthenticationInfo(username, password, getName());}
}package com.lgy.shiro;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 {public String getName() {return "myrealm2";}public boolean supports(AuthenticationToken token) {return token instanceof UsernamePasswordToken; //仅支持UsernamePasswordToken类型的Token}public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String username = (String)token.getPrincipal();  //得到用户名String password = new String((char[])token.getCredentials()); //得到密码if(!"wang".equals(username)) {throw new UnknownAccountException(); //如果用户名错误}if(!"123".equals(password)) {throw new IncorrectCredentialsException(); //如果密码错误}//如果身份认证验证成功,返回一个AuthenticationInfo实现;return new SimpleAuthenticationInfo(username, password, getName());}
}package com.lgy.shiro;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 {public String getName() {return "myrealm3";}public boolean supports(AuthenticationToken token) {return token instanceof UsernamePasswordToken; //仅支持UsernamePasswordToken类型的Token}public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String username = (String)token.getPrincipal();  //得到用户名String password = new String((char[])token.getCredentials()); //得到密码if(!"zhang".equals(username)) {throw new UnknownAccountException(); //如果用户名错误}if(!"123".equals(password)) {throw new IncorrectCredentialsException(); //如果密码错误}//如果身份认证验证成功,返回一个AuthenticationInfo实现;return new SimpleAuthenticationInfo(username + "163@qq.com", password, getName());}
}

封装的login代码如下:

    private void login(String configFile) {//1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManagerFactory<org.apache.shiro.mgt.SecurityManager> factory =new IniSecurityManagerFactory(configFile);//2、得到SecurityManager实例 并绑定给SecurityUtilsorg.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();SecurityUtils.setSecurityManager(securityManager);//3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");subject.login(token);}@Afterpublic void tearDown() throws Exception {ThreadContext.unbindSubject();//退出时请解除绑定Subject到线程 否则对下次测试造成影响}

上面说的:

默认策越(ModularRealmAuthenticator默认使用AtLeastOneSuccessfulStrategy策略。)DEMO

ini文件:

[main]#声明一个realmmyRealm1=com.lgy.shiro.MyRealm1
myRealm2=com.lgy.shiro.MyRealm2
myRealm3=com.lgy.shiro.MyRealm3
#指定securityManager的realms实现securityManager.realms=$myRealm1,$myRealm2,$myRealm3

测试如下:

 @Testpublic void testStrategyWithdefault() {login("classpath:shiro-auth-default.ini");
//        login("classpath:shiro-realm.ini");Subject subject = SecurityUtils.getSubject();/*boolean b = subject.isAuthenticated();System.out.println(b);*///得到一个身份集合,其包含了Realm验证成功的身份信息PrincipalCollection principalCollection = subject.getPrincipals();System.out.println(principalCollection.asList().size());   //返回所有的身份凭证信息//Assert.assertEquals(2, principalCollection.asList().size());}

验证信息是zhang/123, myRealm1和myRealm2是能通过的,打印返回的size为2,正如前面所说的:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy不同,返回所有Realm身份验证成功的认证信息;

FirstSuccessfulStrategy策越,demo如下:

ini文件:

[main]#指定securityManager的authenticator实现
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
securityManager.authenticator=$authenticator
#指定securityManager.authenticator的authenticationStrategy
firstSuccessfulStrategy=org.apache.shiro.authc.pam.FirstSuccessfulStrategy
securityManager.authenticator.authenticationStrategy=$firstSuccessfulStrategy #声明一个realmmyRealm1=com.lgy.shiro.MyRealm1
myRealm2=com.lgy.shiro.MyRealm2
myRealm3=com.lgy.shiro.MyRealm3
#指定securityManager的realms实现securityManager.realms=$myRealm1,$myRealm2,$myRealm3

测试代码:

@Testpublic void testStrategyWithdefirst() { login("classpath:shiro-auth-first.ini");Subject subject = SecurityUtils.getSubject();System.out.println(subject.isAuthenticated());PrincipalCollection principalCollection = subject.getPrincipals();System.out.println(principalCollection.asList().size());}

验证信息是zhang/123, myRealm1和myRealm2是能通过的,打印返回的size为1,正如前面所说的只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他的忽略;

AllSuccessfulStrategy测试,demo如下:

ini文件:

[main]#指定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 #声明一个realmmyRealm1=com.lgy.shiro.MyRealm1
myRealm2=com.lgy.shiro.MyRealm2
myRealm3=com.lgy.shiro.MyRealm3
#指定securityManager的realms实现securityManager.realms=$myRealm1,$myRealm2,$myRealm3

测试文件:

    @Testpublic void testStrategyWithdeAll() { login("classpath:shiro-auth-all.ini");Subject subject = SecurityUtils.getSubject();System.out.println(subject.isAuthenticated());PrincipalCollection principalCollection = subject.getPrincipals();System.out.println(principalCollection.asList().size());}

验证信息是zhang/123, myRealm1和myRealm3是能通过的,但是myRealm2不是通过,所以会抛出异常,测试部通过。 将myRealm2中的wang改为zhang,所以验证能通过,打印为2,为什么?因为返回凭证中myRealm1和myRealm2一样被当做同一个凭证。

关于身份认证中的Authenticator及AuthenticationStrategy相关推荐

  1. 无线局域网(WLAN)系统中身份认证中ikey1000的具体应用

    本文来自:www.kttec.net 你是否幻想过躺在酒店舒适的沙发收发来自远方的客户订单呢?是否想过外出时也可以享受到和家中一样的安全无线接入服务?现在这些都能通过NTT东日本电信电报株式会社为您提 ...

  2. 数据库与身份认证(数据库的基本概念,安装并配置 MySQL,MySQL 的基本使用,在项目中操作 MySQL,前后端的身份认证)

    theme: channing-cyan 数据库与身份认证 1. 数据库的基本概念 1.1 什么是数据库 数据库(database)是用来组织.存储和管理数据的仓库. 当今世界是一个充满着数据的互联网 ...

  3. Shiro系列-Authenticator和AuthenticationStrategy是什么

    导语   之前的博客中分享了关于身份认证以及Realm的内容其中提到了一个比较关键的类,AuthenticationInfo也就是认证信息的类.怎么样去获取到这个身份 认证的信息类呢? 文章目录 Au ...

  4. Shiro(三) 身份认证源码分析与 MD5 盐值加密

    文章目录 1. 身份认证 2. 身份验证的基本流程 3. 身份验证实现 3.1 在 `login.jsp` 添加登录表单 3.2 添加表单提交的 Controller 3.3 完善 Realm 的身份 ...

  5. [信息安全] 4.一次性密码 amp;amp;amp;amp; 身份认证三要素

    在信息安全领域,一般把Cryptography称为密码,而把Password称为口令.日常用户的认知中,以及我们开发人员沟通过程中,绝大多数被称作密码的东西其实都是Password(口令),而不是真正 ...

  6. 浅析Windows域环境身份认证与攻击思路

    文章目录 前言 Kerberos协议 第1步-AS认证获取TGT 第2步-TGS认证获取ST 第3步-服务端服务认证 NTLM 认证 本地认证模式 网络认证模式 内网横向渗透 哈希凭证窃取 内网远程连 ...

  7. [转载]细说ASP.NET Windows身份认证

    细说ASP.NET Windows身份认证 阅读目录 开始 认识ASP.NET Windows身份认证 访问 Active Directory 在ASP.NET中访问Active Directory ...

  8. 细说ASP.NET Forms身份认证

    细说ASP.NET Forms身份认证 阅读目录 开始 ASP.NET身份认证基础 ASP.NET身份认证过程 如何实现登录与注销 保护受限制的页面 登录页不能正常显示的问题 认识Forms身份认证 ...

  9. 详解Spring Security进阶身份认证之UserDetailsService(附源码)

    在上一篇Spring Security身份认证博文中,我们采用了配置文件的方式从数据库中读取用户进行登录.虽然该方式的灵活性相较于静态账号密码的方式灵活了许多,但是将数据库的结构暴露在明显的位置上,绝 ...

最新文章

  1. 自组织映射网络(SOM)+Kohonen自组织网络
  2. dagger android 学习(二):AndroidInjector的使用
  3. 告别2019,写给2020:干好技术,要把握好时光里的每一步
  4. SmartGWT入门,提供出色的GWT界面
  5. python读取json格式的超参数
  6. 小米台灯底座接口很松_小米黑科技,AirPods和小米10 Pro伴侣,ZMI无线充蓝牙音箱体验...
  7. linux用于开发qt java_Linux下Qt程序的打包发布
  8. 线程安全的list之synchronizedList和CopyOnWriteArrayList
  9. 基于SURF算法的图像拼接方法
  10. radius mysql md5_radius协议采用什么传输 radius协议中md5加密函数的参数怎么处理
  11. 最简单的进制转换(三张图片搞定)
  12. 数据仓库实施步骤与关键成功因素
  13. 思科模拟器CiscoPacketTracer下载
  14. mysql 转大写_mysql将字符串转换为大写的方法
  15. Win10 - 使用‘Alt+Tab’不能切换窗口及更改切换风格
  16. Linux命令详解之 cp
  17. java volatile理解
  18. cocos2d-x3.2中用shader使图片背景透明
  19. 【云原生 | Kubernetes 系列】--Envoy熔断
  20. draftsight的热补丁

热门文章

  1. 用迭代器指针改变map容器的值
  2. 浴室预约微信小程序的设计与实现
  3. 6月楼市或迎降价潮 房企策略有调整
  4. iOS中网络编程长连接
  5. Eginx配置(SSL,令牌登录认证,IP白名单,代理转发)
  6. 计算机数列类型,斐波那契(Fibonacci)数列的几种计算机解法
  7. 开发管理---配置管理与文档管理
  8. 淘宝小程序 表单组件checkbox的默认样式修改
  9. 千里走单骑:06-北京到上海骑记--Day5.风雨回家路
  10. linux 每日学一点《用tar来备份ubuntu系统》