一、前言
前一章我们学习了自定义reaml的方式去实现将用户输入的数据,去和数据库中的数据做比较(模拟的数据)。但是不管是把用户的数据存在shiro.ini中,还是数据库中。都是采用着明文存储。很容易的就容易被窃取。那么我们采用将密码加密的方式再存储到数据库中,而进行校验的时候,我们定义一套密文加密解密的流程即可将用户输入的密码转换成特定格式的密文再和数据库中存储的密文校验来判断账号密码是否正确。
二、加密算法的介绍
散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,常见的散列算法如MD5、SHA等。一般进行散列的时候最好提供一个salt(盐)。比如加密密码“admin”,产生的散列值是“21321f297ad212kjad92112” ,可以看到一些MD5解密网站很容易的通过散列值得到密码admin,如果直接对密码进行散列下,很容易就会被破解。此时我们可以加一些只有系统知道的干扰数据,比如用户名和ID(即盐);这样散列的对象是"密码+用户名+ID" ,这样生成的散列值相对来说更加难以破解。
散列算法实例(MD5为例子):
@Test
public void testForShiroHash(){
//假设这个是用户输入的用户名
String username = "luohaizhang";
//假设这个是用户输入的密码
String password = "luo123";
//采用md5的方式加密这个密码
System.out.println(new Md5Hash(password).toString());
//设置盐 , 让生成的md5码变得难以破解
Md5Hash md5Hash = new Md5Hash(password,username);
System.out.println(md5Hash.toString());
//设置迭代的次数,迭代次数越大,加密生成的密文就越难以被破解
Md5Hash md5Hash2 = new Md5Hash(password,username,3);
System.out.println(md5Hash2.toString());
}
//结果由上到下依次是:
50b56e9ea640e0852bf00143a8a39987
f632bd5d914bd7bfafe8790e7a9f5383
789dd2af30294f31beaaeebcca5359b2
三、Reaml密码加密来进行登入登出
步骤
1、自定义加密之后的reaml:重写3个方法。
public class MyCredentialRealm extends AuthorizingRealm {
@Override
public String getName() {
return "MyCredentialRealm";
}
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String)token.getPrincipal();
//假设数据库存储密码为luo123的md5码如下:
if(!"luohaizhang".equals(username)){
return null;
}
String md5Code="789dd2af30294f31beaaeebcca5359b2";
//把用户传递进来的用户名,以及数据库存储对应用户名的md5码,以及加盐要用的字段值,和这个reaml的类名字封装成SimpleAuthenticationInfo对象并返回。
return new SimpleAuthenticationInfo(username,md5Code, ByteSource.Util.bytes("luohaizhang"),getName());
}
这个类主要通过用户传入的账号信息,获取到,并去数据库中检查是否存在这个账号并拿到对应的密码,而其中数据库中存放的密码时md5Code,是用户注册的时候通过md5加密算法计算过了再存入数据库中的,上面的md5Code主要使用的加密算法是md5,通过加盐(salt)也就是将密码,用户名(盐),和散列次数(3次)统一做MD5算法计算,得到最终的结果。
最后返回值SimpleAuthenticationInfo(username,md5Code, ByteSource.Util.bytes("luohaizhang"),getName())
;
其中要写上ByteSource.Util.bytes("luohaizhang")
,这个代表MD5算法加密的时候,该账号使用的盐,它是必备的,用户输入的账号密码,会再次通过md5算法进行计算,如果不指定这个盐,将会导致得出的最终结果和md5Code值不一致!
2、 编写一个shiro-cryptography.ini
,定义reaml加密规则
[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=3
#将凭证匹配器设置到realm
myRealm=自定义Reaml类的地址
myRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$myRealm
上面这个ini文件指定了,当用户输入密码和账户之后,在调用subject.login时,通过md5加密算法的方式,把输入的密码进行加密操作。其中定义了散列算法使用的时md5,以及散列3次。
3. 编写测试类,调用subject.login出发登录认证流程
//shiro加密登录登出
@Test
public void testForShiroCredential(){
Subject subject = ShiroUtils.getSubject("classpath:shiro-credential.ini");
//假设这个是用户输入的用户名
String username = "luohaizhang";
//假设这个是用户输入的密码
String password = "luo123";
//设置迭代的次数,迭代次数越大,加密生成的密文就越难以被破解
try {
//模拟用户名和密码
AuthenticationToken authenticationToken = new UsernamePasswordToken("luohaizhang", password);
//模拟用户登录,会去shiro.ini中对比
subject.login(authenticationToken);
}
catch (UnknownAccountException e){
System.out.println(e.getMessage());
System.out.println("找不到账号");
}
catch (IncorrectCredentialsException e){
System.out.println(e.getMessage());
System.out.println("密码错误");
}
//查看是否登录成功
System.out.println("用户登入后是否存在授权:" + subject.isAuthenticated());
//用户登出
subject.logout();
System.out.println("用户登出后,是否存在授权:" + subject.isAuthenticated());
}