目录树
Shiro加密了解及demo3演示
解锁:数据加密MD5、加salt、进行hash散列
一、了解MYSQL加密算法
mysql的加密算法:
1.不可逆
加密算法:
password ()、md5 ()、encrypt ()、sha5 ()
2.可逆
加密算法:
encode(,)、decode(,):加密解密字符串。函数有两
个参数:被加密或解密的字符串、作为加密或解密基础的密钥。
encode结果是一个二进制字符串,以BLOB类型存储,加密程度比较弱。
——————————————————
aes_encrypt(,)、aes_decrypt(,):加密解密字符串。函数有两
个参数:被加密或解密的字符串、作为加密或解密基础的密钥。encode结果是一个二进制字符串,以BLOB类型存储。
加密完成之后可以用to_base64()
转成可见字符,然后解密时候用from_base64()
转回来.
二、MD5和salt简介和执行流程
2.1 MD5简单介绍
MD5是哈希散列算法,对于MD5而言,有两个特性是很重要的.
第一:明文数据经过散列以后的值是定长的(就是1234经过加密之后, 他的散列之后字符串会变长);
第二:是任意一段明文数据,经过散列以后,其结果必须永远是不变的。
前者的意思是可能存在有两段明文散列以后得到相同的结果。
后者的意思是如果我们散列特定的数据,得到的结果定是相同的。
2.2 salt简单介绍
在家做饭有佐料,在加密领域也有所谓的佐料,只是这是这里的佐料可不是为了味道好,而是为了保护对象的机密性。
往上面撒一些盐,可以有效的解决这个问题,即使用salt加密,它的基本想法是这样的:
1.用户注册时,在密码上撒一些盐。生成一种味道,记住味道。
2.用户再次登陆时,系统在输入的密码上撒盐,闻- -闻,判断是否和原来的味道相同,相同就让你吃饭。
由于验证密码时和最初散列密码时使用相同的盐值,所以salt的存储在数据库
。并且这个值是由系统随机产生的,而非硬编码。这就保证了所要保护对象的机密性。
❤❤❤❤❤
以下是图解(图借鉴):
注册时:
1.用户注册,系统随机产生salt值。
2.将salt值和密码连接起来,生产Hash值。
3.将Hash值和salt值分别存储在数据库中。
登录时:
1.系统根据用户名找到与之对应的密码Hash。
2.将用户输入密码和salt值进行散列。
3.判断生成的Hash值是否和数据库中Hash相同。
2.3 具体的MD5+salt的具体实现流程
2.4 ❤使用加密进行demo演示
1.创项目并导shiro-jar包
2.创SaltTest.java进行测试
public class SaltTest {
public static void main(String[] args) {
//****** 一、只是单纯 MD5加密
//1.实现 MD5加密
//输出结果和在mysql命令行中的结果一样,说明java中的md5算法和数据库中的md5算法底层原理都是一样--哈希散列
Md5Hash md5Hash = new Md5Hash("China");
System.out.println(md5Hash);
//2.生成两个 MD5码,同样加密
Md5Hash md5Hash1 = new Md5Hash("China");
System.out.println(md5Hash1);
if (md5Hash.equals(md5Hash1)){
//如果两个明文不一样,就不会打印,道理都懂
System.out.println("允许登录!!!");
}
//缺点很大,因为多个用户可能存在相同的密码
//****** 二、MD5+salt加密
System.out.println("MD5+salt方式---------------------------------");
System.out.println((int) (Math.random() * 10000));
Md5Hash md5Hash2 = new Md5Hash("China",String.valueOf((int)(Math.random()*10000)));
//后面参数代表salt,使用random方法随机生成,但是数值为0到1之间的一个小数,所以我这里还需要*10000,然后取整
System.out.println(md5Hash2);
//这比第一种好一点,但仍未解决实际问题--因为随机取值,有一个区间,当数量足够大时,就可能出现同样的salt值
//****** 三、MD5+salt+hash散列
System.out.println("MD5+salt+hash散列的方式---能解决重复问题");
System.out.println((int) (Math.random() * 10000));
Md5Hash md5Hash3 = new Md5Hash("China",String.valueOf((int)(Math.random()*10000)),2048);
System.out.println(md5Hash3);
//第三个参数代表我的散列次数,散列次数不一样,salt值最后取得也会不一样。
}
}
运行结果:
所以当了解三种加密形式后,需要去用它们,所以创建一个自定义的realm。
3.自创CustomerMDrealm.java
public class CustomerMDRealm extends AuthorizingRealm {
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken YtyToken) throws AuthenticationException {
System.out.println("已经进入用户认证了-----");
String name = (String) YtyToken.getPrincipal();//获取前端传入的数据信息
System.out.println("这是前端传过来的数据:"+name);
if ("yty".equals(name)){
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
name,"ae54a5c026f31ada088992587d92cb3a",//这是China的MD5加密后的信息
ByteSource.Util.bytes("String.valueOf((int)(Math.random()*10000)"),this.getName());
//ByteSource.Util.bytes后面的参数就是salt值
return simpleAuthenticationInfo;
}
return null;
}
}
4.创Md5Test.javaj进行测试
public class Md5Test {
public static void main(String[] args) {
//1.创建安全管理器
DefaultSecurityManager ytySecurityManager = new DefaultSecurityManager();
//这是之前“明文方式”进行认证
// ytySecurityManager.setRealm(new CustomerMDRealm());//安全管理器设置realm对象
// SecurityUtils.setSecurityManager(ytySecurityManager);//管理工具设置安全管理器
// Subject subject = SecurityUtils.getSubject();//管理工具获得主体subject
// UsernamePasswordToken token = new UsernamePasswordToken("yty", "China");//设置令牌
//2.使用规则匹配器:让它去匹配我们的 MD5
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");//md5:指定的md5规则
//3.指定散列次数
hashedCredentialsMatcher.setHashIterations(2048);
//4.指定自定义的realm使用的规则是上面的规则匹配器
CustomerMDRealm customerMDRealm = new CustomerMDRealm();
customerMDRealm.setCredentialsMatcher(hashedCredentialsMatcher);
//5.剩下操作就是和明文方式一样的
//安全管理器设置realm对象
ytySecurityManager.setRealm(customerMDRealm);
//管理工具设置安全管理器
SecurityUtils.setSecurityManager(ytySecurityManager);
//管理工具获取主体subject
Subject subject = SecurityUtils.getSubject();
//设置令牌
UsernamePasswordToken token = new UsernamePasswordToken("yty","China");
//6.认证操作
System.out.println("认证前的操作---------");
System.out.println(subject.isAuthenticated());
System.out.println("认证后的操作---------");
try {
subject.login(token);
}catch (UnknownAccountException e){
System.out.println("用户账号有问题!!!!!");
}catch (IncorrectCredentialsException e){
System.out.println("密码错误!!!!!!");
}
System.out.println(subject.isAuthenticated());
}
}
测试结果:
❤:之所以错误是因为我加了salt(随机0到1的小数乘10000,然后取整),并且散列了2048次。
当只有MD5加密后,就会判断为true。只需要将CustomerMDRealm中的ByteSource.Util.bytes(“String.valueOf((int)(Math.random()*10000)”)淦掉,就取消了加盐,同时Md5Test不能用规则匹配器,用明文方式验证。
淦掉hashedCredentialsMatcher.setHashIterations(2048);就取消了散列。
2.5 老师演示demo
进行加密的操作:
形成的码:
使用自定义realm的方式来认证我们的MD5+salt+Hash散列实现:
1.使用CustomerMd5Realm.java类去继承我们的AuthorizingRealm
2.去是实现方法; doGetAuthenticationInfo
使用自定义的realm进行认证:
传入的时候使用MD5加密:
认证的时候也是通过加密后的就会认证成功:
只需要在进行认证的时候指定随机盐进行即可:
这个地方指定盐值是从数据库中进行取出来的;以及密码都是进行完随机盐之后的值;只需要在这个地方设置之后;传过来的就会是盐之后的MD5码;使用这个随机盐去进行认证数据;
这个存储到里边的时候是根据散列了次数之后的值;就需要进行传入的时候设置他的匹配规则也是散列次数;
散列次数要对应上:就可以认证