Shiro系列-Authenticator和AuthenticationStrategy是什么

 2023-09-15 阅读 15 评论 0

摘要:导语   之前的博客中分享了关于身份认证以及Realm的内容其中提到了一个比较关键的类,AuthenticationInfo也就是认证信息的类。怎么样去获取到这个身份 认证的信息类呢? 文章目录Authenticator(认证器)AuthenticationStrategy(认证策略)自定义认证策略总结 Au

导语
  之前的博客中分享了关于身份认证以及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是什么、以及认证原理认证策略。简单的了解了一个整个的认证流程。后面将具体的分析每个功能点。敬请期待!

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/3/60438.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息