String msg = “Realm [” + realm + “] was unable to find account data for the submitted AuthenticationToken [” + token + “].”;
throw new UnknownAccountException(msg);
} else {
return info;
}
}
小伙伴们看到这里就明白了,这里调用了realm的getAuthenticationInfo方法,这个方法实际上就是我们自己实现的MyRealm中的getAuthenticationInfo方法。
那如果有多个Realm呢?我们来看看doMultiRealmAuthentication方法的实现,部分源码如下:
protected AuthenticationInfo doMultiRealmAuthentication(Collection realms, AuthenticationToken token) {
AuthenticationStrategy strategy = this.getAuthenticationStrategy();
AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);
Iterator var5 = realms.iterator();
while(var5.hasNext()) {
Realm realm = (Realm)var5.next();
aggregate = strategy.beforeAttempt(realm, token, aggregate);
if(realm.supports(token)) {
AuthenticationInfo info = null;
Throwable t = null;
try {
info = realm.getAuthenticationInfo(token);
} catch (Throwable var11) {
}
aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);
} else {
log.debug(“Realm [{}] does not support token {}. Skipping realm.”, realm, token);
}
}
aggregate = strategy.afterAllAttempts(token, aggregate);
return aggregate;
}
我这里主要来说下这个方法的实现思路:
-
首先获取多Realm认证策略
-
构建一个AuthenticationInfo用来存放一会认证成功之后返回的信息
-
遍历Realm,调用每个Realm中的getAuthenticationInfo方法,看是否能够认证成功
-
每次获取到AuthenticationInfo之后,都调用afterAttempt方法进行结果合并
-
遍历完所有的Realm之后,调用afterAllAttempts进行结果合并,这里主要判断下是否一个都没匹配上
5.3 自由配置认证策略
OK,经过上面的简单解析,小伙伴们对认证策略应该有一个大致的认识了,那么在Shiro中,一共支持三种不同的认证策略,如下:
-
AllSuccessfulStrategy,这个表示所有的Realm都认证成功才算认证成功
-
AtLeastOneSuccessfulStrategy,这个表示只要有一个Realm认证成功就算认证成功,默认即此策略
-
FirstSuccessfulStrategy,这个表示只要第一个Realm认证成功,就算认证成功
配置方式也很简单,在shiro.ini中进行配置,在上面配置的基础上,增加如下配置:
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
securityManager.authenticator=$authenticator
allSuccessfulStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy
securityManager.authenticator.authenticationStrategy=$allSuccessfulStrategy
此时,我们再进行登录测试,则会要求每个Realm都认证通过才算认证通过。
6.1 密码为什么要加密
2011年12月21日,有人在网络上公开了一个包含600万个CSDN用户资料的数据库,数据全部为明文储存,包含用户名、密码以及注册邮箱。事件发生后CSDN在微博、官方网站等渠道发出了声明,解释说此数据库系2009年备份所用,因不明原因泄露,已经向警方报案。后又在官网网站发出了公开道歉信。在接下来的十多天里,金山、网易、京东、当当、新浪等多家公司被卷入到这次事件中。整个事件中最触目惊心的莫过于CSDN把用户密码明文存储,由于很多用户是多个网站共用一个密码,因此一个网站密码泄露就会造成很大的安全隐患。由于有了这么多前车之鉴,我们现在做系统时,密码都要加密处理。
密码加密我们一般会用到散列函数,又称散列算法、哈希函数,是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表。好的散列函数在输入域中很少出现散列冲突。在散列表和数据处理中,不抑制冲突来区别数据,会使得数据库记录更难找到。我们常用的散列函数有如下几种:
- MD5消息摘要算法
MD5消息摘要算法是一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值,用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321中被加以规范。将数据(如一段文字)运算变为另一固定长度值,是散列算法的基础原理。1996年后被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞,因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。
- 安全散列算法
安全散列算法(Secure Hash Algorithm)是一个密码散列函数家族,是FIPS所认证的安全散列算法。能计算出一个数字消息所对应到的,长度固定的字符串(又称消息摘要)的算法。且若输入的消息不同,它们对应到不同字符串的机率很高。SHA家族的算法,由美国国家安全局所设计,并由美国国家标准与技术研究院发布,是美国的政府标准,其分别是:SHA-0:1993年发布,是SHA-1的前身;SHA-1:1995年发布,SHA-1在许多安全协议中广为使用,包括TLS和SSL、PGP、SSH、S/MIME和IPsec,曾被视为是MD5的后继者。但SHA-1的安全性在2000年以后已经不被大多数的加密场景所接受。2017年荷兰密码学研究小组CWI和Google正式宣布攻破了SHA-1;SHA-2:2001年发布,包括SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。虽然至今尚未出现对SHA-2有效的攻击,它的算法跟SHA-1基本上仍然相似;因此有些人开始发展其他替代的散列算法;SHA-3:2015年正式发布,SHA-3并不是要取代SHA-2,因为SHA-2目前并没有出现明显的弱点。由于对MD5出现成功的破解,以及对SHA-0和SHA-1出现理论上破解的方法,NIST感觉需要一个与之前算法不同的,可替换的加密散列算法,也就是现在的SHA-3。
6.2 Shiro中如何加密
Shiro中对以上两种散列算法都提供了支持,对于MD5,Shiro中生成消息摘要的方式如下:
Md5Hash md5Hash = new Md5Hash(“123”, null, 1024);
第一个参数是要生成密码的明文,第二个参数密码的盐值,第三个参数是生成消息摘要的迭代次数。
Shiro中对于安全散列算法的支持如下(支持多种算法,这里我举一个例子):
Sha512Hash sha512Hash = new Sha512Hash(“123”, null, 1024);
这里三个参数含义与上文基本一致,不再赘述。shiro中也提供了通用的算法,如下:
SimpleHash md5 = new SimpleHash(“md5”, “123”, null, 1024);
SimpleHash sha512 = new SimpleHash(“sha-512”, “123”, null, 1024);
当用户注册时,我们可以通过上面的方式对密码进行加密,将加密后的字符串存入数据库中。我这里为了