您好,登录后才能下订单哦!
这期内容当中小编将会给大家带来有关什么是Shiro验证,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
shiro验证:用户需要提供系统理解和信任的某种身份证明(密码、证书等),来证明自己可以登录该系统。
在验证阶段需要先理解几个术语:
Subject(主体)- 可以通俗的理解为访问应用程序的用户。只不过这个用户可以是人、第三方进程等等。
Principals(身份)- 主体的唯一标识属性。例如:身份证号码、手机号、邮箱等等。
Credentials(凭证)- 只有主体知道的安全值。例如:密码、数字证书等等。
Realms(领域)- 数据访问对象。用来获取Principals和Credentials。例如:用户名和密码存放在.ini文件中,可以通过IniRealm获取用户名和密码、用户名和密码存放在数据库中,可以通过JdbcRealm获取用户名和密码等等。
AuthenticationToken(验证令牌)- 结合Principals(身份)和Credentials(凭证)可获得的用户身份令牌。
Authenticator (验证者) - 验证用户验证令牌是否满足要求的科目。
AuthenticationStrategy(身份验证策略)- 表示以何种策略验证用户身份。
如下是从官网获取的验证架构图:
步骤1:收集主题提交的 Principals 和 Credentials 得到 Token,应用程序代码调用Subject.login方法,传入 Token 以进行身份验证动作。
步骤2:Subject实例,通常是 DelegatingSubject(或子类)通过调用 securityManager.login(token)将实际的身份验证工作委托给 SecurityManager 处理
步骤3:SecurityManager 作为基本的组件,接收 Token 然后通过调用 authenticate(token)将其委托给 Authenticator。Authenticator 几乎总是实例 ModularRealmAuthenticator,它支持在身份验证期间协调一个或多个 Realm 实例。
步骤4:如果为应用配置了多个 Realm ,则 ModularRealmAuthenticator 实例将使用其配置的 AuthenticationStrategy 启动多领域身份验证尝试。在调用 Realms 进行身份验证之前,期间或者之后,将调用 AuthenticationStrategy 以允许它对每个 Realm 的结果做出反应。
步骤5:将咨询每个 realm 以查看是否支持已提交的 AuthenticationToken。如果是这样,将使用提交的 Token 调用 realm 的getauthenticationfo方法。getauthenticationfo方法有效地表示该特定领域的单一身份验证尝试。
接下来就开始使用ShiroAPI完成验证操作了。
环境准备:
本文使用 Maven 构建,因此需要一点 Maven 知识。首先准备环境依赖:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> <scope>test</scope> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.3.2</version> </dependency> </dependencies>
添加 junit 和 shiro-core 依赖即可。
初识:登录 / 退出
1、首先准备一些用户身份 / 凭据(shiro.ini)
[users] zhangsan=123 lisi=123
2、测试用例(com.luther.shiro.authenticator.AuthenticateTest)
/** * 底层默认使用ModularRealmAuthenticator验证模块(多realm验证) <br/> * AtLeastOneSuccessfulStrategy验证策略(只要有一个域成功进行身份验证, * 则认为成功。否则失败。会返回所有成功的用户标识) * @author luther * @time 2019年7月5日 上午9:58:39 */ @Test public void testHelloWorld() { // 使用Ini配置文件初始化SecurityManager Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); // 得到SecurityManager实例 并绑定给SecurityUtils SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); // 得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证) Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123"); // 记住用户 token.setRememberMe(Boolean.TRUE); try { // 登录 subject.login(token); System.out.println("登录成功"); } catch (AuthenticationException e) { System.err.println("登录失败,失败原因:"); e.printStackTrace(); } assertTrue("用户已验证成功", subject.isAuthenticated()); // 退出 subject.logout(); }
Realm
realm 接口结构如下,其下方法有:
String getName(); //返回一个唯一的Realm名字 boolean supports(AuthenticationToken token); //判断此Realm是否支持此Token AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException; //根据Token获取认证信息
其中主要默认实现如下:
org.apache.shiro.realm.text.IniRealm:从ini文件获取用户权限等信息。
org.apache.shiro.realm.text.PropertiesRealm:从properties文件获取用户权限等信息。
org.apache.shiro.realm.jdbc.JdbcRealm:从数据库获取用户权限等信息。
以后开发一般继承 AuthorizingRealm(授权)抽象类即可;其继承了 AuthenticatingRealm(即身份验证),而且也间接继承了 CachingRealm(带有缓存实现)。
一、单 Realm 配置
1、自定义 Realm 实现(com.luther.shiro.realm.MyRealm1):
public class MyRealm1 implements Realm { @Override public String getName() { return this.getClass().getName(); } @Override public boolean supports(AuthenticationToken token) { // 只支持UsernamePasswordToken return token instanceof UsernamePasswordToken; } @Override public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token; // 得到用户名 String username = usernamePasswordToken.getUsername(); // 得到密码 String password = new String(usernamePasswordToken.getPassword()); // 户名错误 if (!"zhangsan".equals(username)) { throw new UnknownAccountException(); } // 密码错误 if (!"123".equals(password)) { throw new IncorrectCredentialsException(); } System.out.println("用户" + username + "验证成功"); // 返回 return new SimpleAccount(username, password, getName()); } }
2、ini 配置文件指定自定义 Realm 实现 (shiro-realm.ini)
[main] #声明一个realm myRealm1=com.luther.shiro.realm.MyRealm1 #指定securityManager的realms实现,通过 $name 来引入之前的 realm 定义 securityManager.realms=$myRealm1
3、测试用例请参考 com.luther.shiro.authenticator.AuthenticateTest 的testSingleRealm 测试方法,此方法和 testHelloWorld 除了配置文件其它没有差异。
二、多 Realm 配置
1、ini 配置文件(shiro-multi-realm.ini)
[main] #声明一个realm myRealm1=com.luther.shiro.realm.MyRealm1 #声明一个realm myRealm2=com.luther.shiro.realm.MyRealm2 #指定securityManager的realms实现,通过 $name 来引入之前的 realm 定义 securityManager.realms=$myRealm2,$myRealm1 #此例子中显视的指定了顺序为myRealm2,myRealm1。(可以少指定,比如只指定myRealm2,则myRealm1会被忽略) #如果不指定securityManager.realms,则会按realm的声明(无需设置 realms 属性,其会自动发现)顺序来,此处即为myRealm1,myRealm2。
2、测试用例请参考 com.luther.shiro.authenticator.AuthenticateTest 的 testMutiRealm 测试方法。
Authenticator 及 AuthenticationStrategy
一、Authenticator 及 AuthenticationStrategy简介
Authenticator 的职责是验证用户帐号,是 Shiro API 中身份验证核心的入口点(默认实现为 ModularRealmAuthenticator ),其方法定义为:
public AuthenticationInfo authenticate(AuthenticationToken authenticationToken) throws AuthenticationException;
如果验证成功,将返回 AuthenticationInfo 验证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的 AuthenticationException 实现。
AuthenticationStrategy 则是 Authenticator 进行验证时的验证策略 (默认实现为:AtLeastOneSuccessfulStrategy),Shiro API 自带的策略有以下三种:
FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第一个 Realm 身份验证成功的认证信息,其他的忽略。
AtLeastOneSuccessfulStrategy:只要有一个 Realm 验证成功即可,和 FirstSuccessfulStrategy 不同,返回所有 Realm 身份验证成功的认证信息。
AllSuccessfulStrategy:所有 Realm 验证成功才算成功,且返回所有 Realm 身份验证成功的认证信息,如果有一个失败就失败了。
二、Authenticator 及 AuthenticationStrategy演示
假设我们有三个 realm:
myRealm1: 用户名/密码为 zhangsan/123 时成功,且返回身份/凭据为 zhangsan/123;
myRealm2: 用户名/密码为 lisi/123 时成功,且返回身份/凭据为 lisi/123;
myRealm3: 用户名/密码为 zhangsan/123 时成功,且返回身份/凭据为 zhangsan.qq/123
1、通用化登录逻辑
private void authentition(String iniConfigPath, String username, String password) { // 使用Ini配置文件初始化SecurityManager Factory<SecurityManager> factory = new IniSecurityManagerFactory(iniConfigPath); // 得到SecurityManager实例 并绑定给SecurityUtils SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); // 得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证) Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 记住用户 token.setRememberMe(Boolean.TRUE); try { // 登录 subject.login(token); System.out.println("用户zhangsan登录成功,其身份标识为" + subject.getPrincipals()); } catch (AuthenticationException e) { System.err.println("登录失败,失败原因:"); e.printStackTrace(); } assertTrue("用户已验证成功", subject.isAuthenticated()); // 退出 subject.logout(); }
2、测试 AtLeastOneSuccessfulStrategy
2.1 ini 配置文件 (shiro-firstSuccessfulStrategy.ini)
[main] #指定securityManager的authenticator实现,可以不指定,因为其默认实现就是ModularRealmAuthenticator #authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator #securityManager.authenticator=$authenticator #指定securityManager.authenticator的authenticationStrategy,可以不指定,因为其默认实现就是AtLeastOneSuccessfulStrategy #authenticationStrategy=org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy #securityManager.authenticator.authenticationStrategy=$authenticationStrategy #声明一个realm myRealm1=com.luther.shiro.realm.MyRealm1 #声明一个realm myRealm2=com.luther.shiro.realm.MyRealm2 #声明一个realm myRealm3=com.luther.shiro.realm.MyRealm3 #指定securityManager的realms实现,通过 $name 来引入之前的 realm 定义 securityManager.realms=$myRealm3,$myRealm2,$myRealm1
2.2 测试代码
/** * 演示AtLeastOneSuccessfulStrategy的效果 * 每个realm都会进行验证,并返回全部的成功用户标识 * @author luther * @time 2019年7月5日 上午11:30:24 */ @Test public void testAtLeastOneSuccessfulStrategy() { authentition("classpath:shiro-atLeastOneSuccessfulStrategy.ini", "zhangsan", "123"); }
2.3 测试结果
开始验证com.luther.shiro.realm.MyRealm3 com.luther.shiro.realm.MyRealm3 - 用户zhangsan验证成功 开始验证com.luther.shiro.realm.MyRealm2 开始验证com.luther.shiro.realm.MyRealm1 com.luther.shiro.realm.MyRealm1 - 用户zhangsan验证成功 用户zhangsan登录成功,其身份标识为zhangsan.qq,zhangsan
即 PrincipalCollection 包含了 zhangsan 和 zhangsan.qq 身份信息。
3、测试 AllSuccessfulStrategy
3.1 ini 配置文件 (shiro-allSuccessfulStrategy.ini)
[main] #指定securityManager的authenticator实现,可以不指定,因为其默认实现就是ModularRealmAuthenticator #authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator #securityManager.authenticator=$authenticator #指定securityManager.authenticator的authenticationStrategy为AllSuccessfulStrategy authenticationStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy securityManager.authenticator.authenticationStrategy=$authenticationStrategy #声明一个realm myRealm1=com.luther.shiro.realm.MyRealm1 #声明一个realm myRealm2=com.luther.shiro.realm.MyRealm2 #声明一个realm myRealm3=com.luther.shiro.realm.MyRealm3 #指定securityManager的realms实现,通过 $name 来引入之前的 realm 定义 securityManager.realms=$myRealm3,$myRealm2,$myRealm1
3.2 测试代码
/** * 演示AllSuccessfulStrategy的效果 * 依次对每个realm进行验证,验证通过下一个,验证失败,直接返回失败验证原因,只有全部成功时才返回全部的用户标识 * 每个realm都会进行验证,并返回全部的成功用户标识 * @author luther * @time 2019年7月5日 上午11:30:24 */ @Test public void testAllSuccessfulStrategy() { authentition("classpath:shiro-allSuccessfulStrategy.ini", "zhangsan", "123"); }
3.3 测试结果
开始验证com.luther.shiro.realm.MyRealm3 com.luther.shiro.realm.MyRealm3 - 用户zhangsan验证成功 开始验证com.luther.shiro.realm.MyRealm2 登录失败,失败原因: org.apache.shiro.authc.UnknownAccountException at com.luther.shiro.realm.MyRealm2.getAuthenticationInfo(MyRealm2.java:42) ······
4、测试 firstSuccessfulStrategy
4.1 ini 配置文件 (shiro-firstSuccessfulStrategy.ini)
[main] #指定securityManager的authenticator实现,可以不指定,因为其默认实现就是ModularRealmAuthenticator #authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator #securityManager.authenticator=$authenticator #指定securityManager.authenticator的authenticationStrategy为FirstSuccessfulStrategy authenticationStrategy=org.apache.shiro.authc.pam.FirstSuccessfulStrategy securityManager.authenticator.authenticationStrategy=$authenticationStrategy #声明一个realm myRealm1=com.luther.shiro.realm.MyRealm1 #声明一个realm myRealm2=com.luther.shiro.realm.MyRealm2 #声明一个realm myRealm3=com.luther.shiro.realm.MyRealm3 #指定securityManager的realms实现,通过 $name 来引入之前的 realm 定义 securityManager.realms=$myRealm3,$myRealm2,$myRealm1
4.2 测试代码
/** * 演示FirstSuccessfulStrategy的效果 * 会对每个realm进行验证,全部验证完后会返回验证成功的第一个用户标识(此处需注意,不是依次验证立马返回,而是全部验证再返回) * @author luther * @time 2019年7月5日 上午11:30:24 */ @Test public void testFirstSuccessfulStrategy() { authentition("classpath:shiro-firstSuccessfulStrategy.ini", "zhangsan", "123"); }
4.3 测试结果
开始验证com.luther.shiro.realm.MyRealm3 com.luther.shiro.realm.MyRealm3 - 用户zhangsan验证成功 开始验证com.luther.shiro.realm.MyRealm2 开始验证com.luther.shiro.realm.MyRealm1 com.luther.shiro.realm.MyRealm1 - 用户zhangsan验证成功 用户zhangsan登录成功,其身份标识为zhangsan.qq
以上已经演示了API自带的验证策略,以下稍微演示下自定义的验证策略。
自定义 AuthenticationStrategy 实现之前,首先简单看其 API:
//在所有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;
5、自定义验证策略(com.luther.shiro.authenticationStrategy.LastSuccessfulStrategy)
/** * 验证所有realm,并返回最后一个验证通过的身份标识 * @author luther * @time 2019年7月5日 下午4:12:31 */ public class LastSuccessfulStrategy extends AbstractAuthenticationStrategy { // 第一种方案修改afterAttempt方法 @Override public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException { return singleRealmInfo; } // // 第二种方案修改merge方法 // @Override // protected AuthenticationInfo merge(AuthenticationInfo info, AuthenticationInfo aggregate) { // if (info != null) { // aggregate = info; // } // // return aggregate; // } }
5.1 ini 配置文件 (shiro-lastSuccessfulStrategy.ini)
[main] #指定securityManager的authenticator实现,可以不指定,因为其默认实现就是ModularRealmAuthenticator #authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator #securityManager.authenticator=$authenticator #指定securityManager.authenticator的authenticationStrategy为自定义的LastSuccessfulStrategy authenticationStrategy=com.luther.shiro.authenticationStrategy.LastSuccessfulStrategy securityManager.authenticator.authenticationStrategy=$authenticationStrategy #声明一个realm myRealm1=com.luther.shiro.realm.MyRealm1 #声明一个realm myRealm2=com.luther.shiro.realm.MyRealm2 #声明一个realm myRealm3=com.luther.shiro.realm.MyRealm3 #指定securityManager的realms实现,通过 $name 来引入之前的 realm 定义 securityManager.realms=$myRealm3,$myRealm2,$myRealm1
5.2 测试代码
/** * 演示自定义的LastSuccessfulStrategy的效果 * 会对每个realm进行验证,全部验证完后会返回验证成功的最后一个用户标识 * @author luther * @time 2019年7月5日 上午11:30:24 */ @Test public void testLastSuccessfulStrategy() { authentition("classpath:shiro-lastSuccessfulStrategy.ini", "zhangsan", "123"); }
5.3 测试结果
开始验证com.luther.shiro.realm.MyRealm3 com.luther.shiro.realm.MyRealm3 - 用户zhangsan验证成功 开始验证com.luther.shiro.realm.MyRealm2 开始验证com.luther.shiro.realm.MyRealm1 com.luther.shiro.realm.MyRealm1 - 用户zhangsan验证成功 用户zhangsan登录成功,其身份标识为zhangsan
上述就是小编为大家分享的什么是Shiro验证了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。