Shiro系列-Authenticator和AuthenticationStrategy是什么
导语
之前的博客中分享了关于身份认证以及Realm的内容其中提到了一个比较关键的类,AuthenticationInfo也就是认证信息的类。怎么样去获取到这个身份 认证的信息类呢?
文章目录
- Authenticator(认证器)
- AuthenticationStrategy(认证策略)
- 自定义认证策略
- 总结
Authenticator(认证器)
之前的内容中提到过如果用户通过login方法认证失败之后会返回AuthenticationException类的异常以及其子类的异常。还总结过这些异常分别是什么?这里首先来看一下,Authenticator其实是一个接口,其中提供了一个用来认证身份信息的方法。通过传入的AuthenticationToken来进行身份认证。
public interface Authenticator {public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)throws AuthenticationException;
}
如果验证成功将会返回一个AuthenticationInfo的验证信息,这个信息中包含了身份以及凭证。
那么下面就来分析一下接口的实现类有那些。从下面截图中可以看到SecurityManager接口就继承了Authenticator,当然还有一个ModularRealmAuthenticator也实现了,它的作用就是将其委托给多个Realm进行验证,验证规则是通过AuthenticationStrategy接口进行制定的。
而AuthenticationStrategy的默认实现有如下一些
其中
FirstSuccessfulStrategy:表示只要一个Realm验证成功就可以了,只返回第一个Realm身份验证成功的认证信息,将其他的验证忽略。
AtLeastOneSuccessfulStrategy:表示只要有一个Realm验证成功即可,和上面一个不同的是,返回所有的Realm身份验证成功的认证信息。
AllSuccessfulStrategy:所有的Realm都要验证成功才算成功,并且返回所有的Realm身份验证成功的验证信息,如果其中有一个失败就失败了。
在ModularRealmAuthenticator中默认使用的策略是AtLeastOneSuccessfulStrategy。也就是说假如这里有三个Realm
- myRealm1 用户名/密码为test/123时返回成功,并且返回test/123
- myRealm2 用户名/密码为admin/123时返回成功,并且返回admin/123
- myRealm3 用户名/密码为test/123时返回成功,但是返回的身份认证是test@163.com/123
从上面三个返回结果来看,第三个出现了身份认证返回的数据不匹配了。那么会出现什么样的情况呢?接下来就来看看AuthenticationStrategy
AuthenticationStrategy(认证策略)
首先配置如下的策略
# 指定SecurityManager的Authenticator实现
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
securityManager.authenticator=$authenticator
# 指定SecurityManager.authenticator 的实现策略
allSuccessfulStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy
securityManager.authenticator.authenticationStrategy=$allSuccessfulStrategy# 声明realm
myRealm1=com.nihui.shiro.realm.realmconfig.MyRealm1
myRealm2=com.nihui.shiro.realm.realmconfig.MyRealm2
myRealm3=com.nihui.shiro.realm.realmconfig.MyRealm3
# 指定SecurityManager的Realm实现
securityManager.realms=$myRealm1,$myRealm2
1、修改之前代码中的内容创建三个Realm如下
Realm1
public class MyRealm1 implements Realm {public String getName() {return "myrealm1";}public boolean supports(AuthenticationToken token) {//仅仅支持一个UsernamePaasswordTokereturn token instanceof UsernamePasswordToken;}public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//获取到用户名和密码String username = (String) token.getPrincipal();String password = new String((char[]) token.getCredentials());if (!"test".equals(username)){//用户名错误throw new UnknownAccountException();}if (!"123".equals(password)){//密码错误throw new IncorrectCredentialsException();}//如果身份认证验证成功,返回一个AuthenticationInforeturn new SimpleAuthenticationInfo(username,password,getName());}
}
Realm2
public class MyRealm2 implements Realm {public String getName() {return "myrealm2";}public boolean supports(AuthenticationToken token) {//仅仅支持一个UsernamePaasswordTokereturn token instanceof UsernamePasswordToken;}public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//获取到用户名和密码String username = (String) token.getPrincipal();String password = new String((char[]) token.getCredentials());if (!"admin".equals(username)){//用户名错误throw new UnknownAccountException();}if (!"123".equals(password)){//密码错误throw new IncorrectCredentialsException();}//如果身份认证验证成功,返回一个AuthenticationInforeturn new SimpleAuthenticationInfo(username,password,getName());}
}
Realm3
public class MyRealm3 implements Realm {public String getName() {return "myrealm3";}public boolean supports(AuthenticationToken token) {//仅仅支持一个UsernamePaasswordTokereturn token instanceof UsernamePasswordToken;}public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//获取到用户名和密码String username = (String) token.getPrincipal();String password = new String((char[]) token.getCredentials());if (!"test".equals(username)){//用户名错误throw new UnknownAccountException();}if (!"123".equals(password)){//密码错误throw new IncorrectCredentialsException();}//如果身份认证验证成功,返回一个AuthenticationInfousername = "test@163.com";return new SimpleAuthenticationInfo(username,password,getName());}
}
特别注意的是Realm3 中有如下的改动,当用户认证成功之后,他所返回的认证信息是用户邮箱。
username = "test@163.com";
return new SimpleAuthenticationInfo(username,password,getName());
2、编写测试类
public class AuthenticatorTest {public static void main(String[] args) {//测试成功testAllSuccessFulStrategyWithSuccess();}//测试成功public static void testAllSuccessFulStrategyWithSuccess(){login("classpath:shiro-authenticator-all-success.ini");Subject subject = SecurityUtils.getSubject();PrincipalCollection principalCollection = subject.getPrincipals();System.out.println(principalCollection.asList().size());}private static void login(String configFile){Factory<SecurityManager> factory = new IniSecurityManagerFactory(configFile);SecurityManager securityManager = factory.getInstance();SecurityUtils.setSecurityManager(securityManager);Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken("test","123");subject.login(token);}
}
会看到上代码最后输出的结果是2,那么为什么会是2呢?其实细心的就会返现其实在PrincipalCollection作为返回值返回的时候,都知道Principal是用户认证成功之后的信息,与用户认证信息是没有关系的。所以说只要用户名和密码操作完成之后,就会在PrincipalCollection放置一条数据,如果当Realm1和Realm3返回的数据相同的时候,PrincipalCollection中只存在一条信息。
自定义认证策略
既然是这样那么就可以实现自定义的认证策略那么如何实现自定义的认证策略呢?首先来分析一下已有的认证策略接口。org.apache.shiro.authc.pam.AuthenticationStrategy
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实例都是无状态的,所以每次通过接口将相应的认证信息传入下一次流程:通过上面的接口可以合并/返回第一个验证成功的信息。
一般情况自定义的AuthenticationStrategy可以参考它的抽象实现AbstractAuthenticationStrategy也可以参考具体实现来操作。
总结
通过之前三篇博客,介绍了用户身份认证中的三个重要的点,如何进行身份验证、Realm是什么、以及认证原理认证策略。简单的了解了一个整个的认证流程。后面将具体的分析每个功能点。敬请期待!
Shiro系列-Authenticator和AuthenticationStrategy是什么相关推荐
- Shiro系列(三)--- Shiro身份验证和授权
继续我们shiro系列博客相关的学习笔记,各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟.多谢! 目录 身份验证 示例 创建SecurityManager ...
- shiro系列二、身份验证和授权
一.身份验证 先来看看身份验证的流程 流程如下: 1.首先调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtil ...
- 关于身份认证中的Authenticator及AuthenticationStrategy
在流程图3中,有Authenticator和AuthenticationStrategy2个接口. Authenticator的职责是验证用户帐号,是Shiro API中身份验证核心的入口点: 它只有 ...
- Shiro系列-Shiro如何实现身份验证
导语 下面就来按照顺序依次介绍一下Shiro的使用场景,场景代码后续会放到GitHub上面希望大家可以多多支持.首先先来介绍一下Shiro的身份认证. 文章目录 Shiro身份验证 入门小例子 环 ...
- shiro系列-1.总览
1 介绍 什么是shiro? Apache Shiro是一个功能强大.灵活的,开源的安全框架.它可以干净利落地处理身份验证.授权.企业会话管理和加密. shiro能做什么? 验证身份 用户访问权限控制 ...
- shiro系列一、认识shiro
Apache Shiro是Java的一个安全框架.目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大 ...
- Shiro系列-Shiro的怎么进行授权操作
导语 之前的分享中,提到了Shiro的简单介绍,知道了Shiro是什么,作用是什么,以及用户身份认证.那么完成用户身份认证之后又需要干点啥呢?在用户身份认证之后接下来就要要根据用户身份角色信息进行 ...
- Shiro系列-Shiro简介
导语 Apache Shiro是一个Java安全框架,现在在很多的场景下使用Shiro的人越来越多.因为它与Spring Security 相比较来说相对比较简单,从功能上来讲也没有Spring ...
- Shiro系列-Shiro中Realm如何使用
导语 之前的分享中,了解到了用户身份认证,在说用户认证的时候提到了一个概念就是Realm,在之前的入门分享中提到了,Realm其实就是一个安全数据源,那么怎么样使用这个安全数据源呢?下面就来一起研 ...
最新文章
- linux字符串转为二进制,Linux printf将十进制转换为二进制?
- [转]马云在《赢在中国》对创业者的经典点评
- python 获取闭包函数的参数
- u-boot分析之内核启动(五)
- 一篇对伪共享、缓存行填充和CPU缓存讲的很透彻的文章
- matlab dsp实验报告,matlab实验报告14.pdf
- shell脚本实现检測回文字符串
- LeetCode(28): 实现strStr()
- find python列表_Python基础知识(7)list列表各种操作
- 计算机组装技术的心得与体会,计算机技术心得体会
- Beetl模板 [记录]
- Virtual Serial Port Driver虚拟串口vspd v7.2 下载及破解方法
- C# 图片处理之:旋转图片任意角度
- CSS样式表操作及选择器定义
- 超级外链工具_慈溪SEO优化_豆瓣外链:豆瓣小组做外链的注意事项?
- CE6870 添加ipv6 策略路由失败问题解决
- Win10重装系统后进行初始化
- 产品经理必备原型工具Axure RP 8自定义元件库
- 【认识 NVIDIA GPU】GPU相关基础概念介绍
- elasticsearch-ingest-pipeline总结