一、MD5算法的介绍
MD5算法用于保证信息传输完整的一致。MD5是一个不可逆的字符串变换算法,它将任意长度的“字节串”变换成一个128bit的大整数。即使看到源程序和算法描述,也无法将一个MD5的值变换回原始的字符串。从数学原理上说,是因为原始的字符串有无穷多个。
MD5的典型应用是对一段信息串产生签名,以防止被“篡改”。比方说,将一段内容写在一个文本文件中,并对这个文本文件产生一个MD5的值并进行记录,然后可以传播这个文件给别人,别人如果修改了文件中的任何内容,当我们对这个文件重新计算MD5时就会发现文件内容被修改。如果再有一个第三方的认证机构,用MD5还可以防止文件作者的“抵赖”,这就是所谓的数字签名应用。
MD5还广泛用于加密和解密技术上,在很多操作系统中,用户的密码是以MD5值(或类似的其它算法)的方式保存的,用户Login的时候,系统是把用户输入的密码计算成MD5值,然后再去和系统中保存的MD5值进行比较,而系统并不“知道”用户的密码是什么。
如下图:MD5加密在service层实现,并将加密后的密码存储到数据库中。当用户登录的时候,从数据库中读取密码与用户输入的密码进行对比。
二、Shiro中MD5的使用
首先通过一个程序演示一下Md5:
package org.example.shiro;
import org.apache.shiro.crypto.hash.Md5Hash;
public class TestMD5 {
public static void main(String[] args) {
//MD5的使用
Md5Hash md5Hash1 = new Md5Hash("admin");
System.out.println(md5Hash1.toHex());
//使用MD5+salt
Md5Hash md5Hash2 = new Md5Hash("admin", "vasdg");
System.out.println(md5Hash2.toHex());
//使用MD5+Salt+hash散列
Md5Hash md5Hash3 = new Md5Hash("admin", "vasdg", 1024);
System.out.println(md5Hash3.toHex());
}
}
执行结果:
21232f297a57a5a743894a0e4a801fc3
a5599b5184c1aadc0877cd76cb93a969
578422679b493529e22c5495c8b3db3c
md5Hash1只是Shiro中md5算法的一个简单使用, md5Hash2里加了salt处理,即任意拼接一个字符串"vasdg",默认hash次数为1,而md5Hash3是在md5Hash2的基础上hash次数为1024次。
那么在Shiro中如何使用Md5呢?
首先需要设置realm使用hash凭证匹配器
package org.example.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
public class TestMD5 {
public static void main(String[] args) {
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//注入realm
UserMD5Realm realm = new UserMD5Realm();
//设置realm使用hash凭证匹配器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");
realm.setCredentialsMatcher(credentialsMatcher);
//设置Realm
defaultSecurityManager.setRealm(realm);
//将安装工具类中设置默认安全管理器
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取主体对象
Subject subject = SecurityUtils.getSubject();
//创建token令牌
UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin");
try{
//用户登录
subject.login(token);
System.out.println("登陆成功");
}catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("用户名错误");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误");
}
}
}
其中,设置hash凭证匹配器代码如下:
//设置realm使用hash凭证匹配器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");
realm.setCredentialsMatcher(credentialsMatcher);
从上面代码可以看出:我们设置realm使用hash凭证匹配器,并指定加密算法为md5。
其次查询数据库,对比密码即可。
package org.example.shiro;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
public class UserMD5Realm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取身份信息
String principal = (String) authenticationToken.getPrincipal();
if("admin".equals(principal)){
//这里的密码模拟是从数据库中读到的
String password = "a5599b5184c1aadc0877cd76cb93a969";
return new SimpleAuthenticationInfo(principal, password, ByteSource.Util.bytes("vasdg"), this.getName());
}
return null;
}
}
执行结果:
以上展示了md5加密的简单过程,其hash次数为1。基于上述代码,增加散列次数:
//设置散列次数
credentialsMatcher.setHashIterations(1024);
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取身份信息
String principal = (String) authenticationToken.getPrincipal();
if("admin".equals(principal)){
//这里的密码模拟是从数据库中读到的
String password = "578422679b493529e22c5495c8b3db3c";
return new SimpleAuthenticationInfo(principal, password, ByteSource.Util.bytes("vasdg"), this.getName());
}
return null;
}
执行结果: