Shiro的加密管理(六)

简介: Shiro的加密管理(六)

一. 密码为什么要加密?


密码为什么要加密,当然是为了密码的安全性。 由于MD5加密算法是不可逆的,即不能由密文推算出明文,所以即使数据库管理员或者入侵者知道了你存储在数据库里面的密码,也不会知道你真实的密码,这样就保证了你账户的安全性。


然而,单纯的只使用 MD5加密,就安全吗?


第一, MD5虽然不能加密,但是却可以碰撞,从而得出原来的明文密码。 如 你的密码是 1234,或者 abc 这样的简单密码,即使通过 MD5进行加密,也同样是可以被破解出来的。


第二, 同一个程序,所使用的加密算法是一致的。注册者所拥有的初始密码是一致的。

那么,当两个人都没有改变密码的时候,两者存储在数据库里面的密码是一致的。

即 A存储在数据库里面的密码是 AiTsc3dGCn7L6+GJmB7IuQ==, B存储在数据库里面的密码也是 AiTsc3dGCn7L6+GJmB7IuQ==,那么A,B的密码都没有改变, 其他人使用A,B的账户+初始密码,也是可以登录进去的。


这样,也是不安全的。 希望能有一个标识,md5在加密的时候,可以考虑到这个标识, 这个标识应该是唯一的, 如用户的id,用户的唯一编号。


这个标识,专业上讲叫做 盐(salt)。


用户在设置自己的密码时,为了方便记忆和输入,通常都比较好记, 即使加上了盐,有时候,也很容易破解。 如 用户的密码是1234, 盐是abc,


那么 1234abc 这个密码很有可能也会被碰撞出来。 有人就想,能不能把 1234abc 这个密码加密后,再加密呢?多加密几次不就行了吗?


1234 不加密,很容易破解。 1234加盐abc 变成 1234abc 时,也可能很容易破解, 但如果把MD5加密 1234abc 之后的那个密码再加密,是否就不容易破解呢?


如果将MD5加密1234abc后的再加密的字符串再进行加密,是否就更不容易被破解呢? 如果这个次数,足够大呢?


其实,这也是MD5 加密的三个阶段。


二. MD5 加密的简单小例子


package com.yjl.md5;
import org.apache.shiro.crypto.hash.Md5Hash;
public class MD5Demo {
  public static void main(String[] args) {
    //普通md5加密 md5(明文)
    Md5Hash md51=new Md5Hash("1234");
    System.out.println("1234加密:"+md51.toBase64());
    //加盐, 是  md5(明文+盐)
    Md5Hash md52=new Md5Hash("1234","yuejl");
    System.out.println("1234加密 盐是yuejl:"+md52.toBase64());
    //加密10次, 是md5^10(明文+盐)
    Md5Hash md53=new Md5Hash("1234","yuejl",10);
    System.out.println("加密10次:"+md53.toBase64());
    //项目中,通过使用第三种 
    Md5Hash md54=new Md5Hash("1234","admin",10);
    System.out.println("加密10次:"+md54.toBase64());
  }
}


控制台打印输出:


20200513162939411.png


接下来,将 MD5 加密 实际运用到项目里面。


运用的是 上一章节的例子, 自定义 Realm.


三. MD5 加密进行认证


三.一 数据库准备


在 user 表里面添加一列, salt 。 (数据库中加盐即可,加密的次数,一般是固化的)

salt 的值 是 用户的编号值, 密码password 需要改写, 通过 上面的例子运行,得到相应的结果,进行更新密码。


//加密10次, 是md5^10(明文+盐)
Md5Hash md53=new Md5Hash("1234","admin",10);
System.out.println("admin 加密10次:"+md53.toBase64());
//项目中,通过使用第三种 
Md5Hash md54=new Md5Hash("1234","yuejl",10);
System.out.println("yuejl 加密10次:"+md54.toBase64());
Md5Hash md55=new Md5Hash("1234","yuezl",10);
System.out.println("yuezl 加密10次:"+md55.toBase64());


20200513162947692.png


这是以前的密码:


20200513162953441.png


内容是: 81dc9bdb52d04dc20036dbd8313ed055


这是现在的密码:


20200513162958872.png


完全不是一个级别的。


三.二 pojo包下的 user 实体添加 一个 salt 字段


private String salt;


添加这个字段,并实现 setter 和getter 方法。


三.三 编写自定义 Realm, 实现认证和授权


MyMd5Realm.java


package com.yjl.md5;
import java.util.List;
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.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import com.yjl.dao.PrivilegeDao;
import com.yjl.dao.RoleDao;
import com.yjl.dao.UserDao;
import com.yjl.dao.impl.PrivilegeDaoImpl;
import com.yjl.dao.impl.RoleDaoImpl;
import com.yjl.dao.impl.UserDaoImpl;
import com.yjl.pojo.Privilege;
import com.yjl.pojo.Role;
import com.yjl.pojo.User;
public class MyMd5Realm extends AuthorizingRealm{
  private UserDao userDao=new UserDaoImpl();
  private PrivilegeDao privilegeDao=new PrivilegeDaoImpl();
  private RoleDao roleDao=new RoleDaoImpl();
  @Override
  public String getName() {
    return "MyMd5Realm";
  }
  //授权
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection paramPrincipalCollection) {
    System.out.println("进入授权");
    User user=(User)paramPrincipalCollection.getPrimaryPrincipal();
    System.out.println("输出登录的用户编号:"+user.getCode());
    //查询权限
    String priSql="select a.* from privilege a where a.id in ( select rp.pid from user_role ur "
        + " left join role_privilege rp "
        +"on ur.rid=rp.rid  where ur.uid=? ) and a.type=?";
    List<Privilege> privilegeList= privilegeDao.findInfosBySql(priSql,user.getId(),2);
    SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();    
    for(Privilege pri:privilegeList){
      if(pri.getPercode()!=null&&!("".equals(pri.getPercode()))){ 
        simpleAuthorizationInfo.addStringPermission(pri.getPercode());
      }
    }
    //查询角色
    String roleSql="select a.* from role a left join user_role b on a.id=b.rid "
        +" where b.uid=?";
    List<Role> roleList=roleDao.findInfosBySql(roleSql, user.getId());
    for(Role role:roleList){
      simpleAuthorizationInfo.addRole(role.getId()+"");
    }
    return simpleAuthorizationInfo; 
  }
  //认证
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken paramAuthenticationToken)
      throws AuthenticationException {
    System.out.println("进入认证");
    String code=(String)paramAuthenticationToken.getPrincipal();
    //根据用户名,去查询相应的数据
    User user=userDao.getInfoByNameAndValue("select * from user","code", code);
    if(user==null){
      //没有查询出来
      return null;
    }
    SimpleAuthenticationInfo simpleAuthenticationInfo=
        new SimpleAuthenticationInfo(user,user.getPassword(),
            //传入转换后的盐
            ByteSource.Util.bytes(user.getSalt()),getName());
    return simpleAuthenticationInfo;
  }
}


三.四 创建配置文件 customermd5.ini


里面配置加密算法和自定义Realm


[main]
#加密类
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#加密方式
credentialsMatcher.hashAlgorithmName=md5
#加密次数
credentialsMatcher.hashIterations=10
#存储散列后的密码是否为16进制 
credentialsMatcher.storedCredentialsHexEncoded=false
#配置自定义realm
myRealm=com.yjl.md5.MyMd5Realm
#配置加密
myRealm.credentialsMatcher=$credentialsMatcher
#注入自定义的realm
securityManager.realm=$myRealm


三.五 编写测试类 MyRealmMd5Test


package com.yjl.md5;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import com.yjl.pojo.User;
public class MyRealmMd5Test {
  public static void main(String[] args) throws Exception{
    //1. 创建factory
    Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:customermd5.ini");
    //2 获取实例
    SecurityManager securityManager=factory.getInstance();
    //3 设置
    SecurityUtils.setSecurityManager(securityManager);
    //4. 获取 Subject
    Subject subject=SecurityUtils.getSubject();
    //5. 设置token
    //直接输入明文 1234 密码即可,不需要手动加密
    UsernamePasswordToken token=new UsernamePasswordToken("yuejl","1234");
    //6. 执行登录操作
    try{
      subject.login(token); 
      User user=(User)subject.getPrincipal();
      //可以将这个user 设置到session 里面
      System.out.println(user.toString()+"登录成功");
      //admin 是管理员
      boolean flag=subject.hasRole("1");
      System.out.println("是否拥有角色 管理员:"+flag);
      //关于角色验证的那些方法,都可以使用
      flag=subject.hasRole("2");
      System.out.println("是否拥有角色 经理:"+flag);
      //admin有删除权限,没有添加权限 
      flag=subject.isPermitted("dept:add");
      System.out.println("是否拥有权限 dept:add "+flag);
      flag=subject.isPermitted("dept:delete");
      System.out.println("是否拥有权限 dept:delete "+flag);
    }catch(Exception e){
      e.printStackTrace();
      System.out.println("用户名或者密码不正确");
    }
  }
}


控制台打印输出:


20200513163051113.png


输入传入的密码是 12345, 错误的密码


UsernamePasswordToken token=new UsernamePasswordToken("yuejl","12345");


20200513163058354.png


密码加密是成功的。


本章节代码链接为:


链接:https://pan.baidu.com/s/1clhY9jW0qKyE3ZFA0gp3uA 
提取码:9bjw


谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!

相关文章
|
1月前
|
存储 搜索推荐 安全
ISO/IEC 23001:数字版权管理的加密标准(三)
ISO/IEC 23001:数字版权管理的加密标准(三)
74 0
|
1月前
|
存储 编解码 算法
ISO/IEC 23001:数字版权管理的加密标准(一)
ISO/IEC 23001:数字版权管理的加密标准(一)
74 0
|
1月前
|
存储 JSON 安全
使用shiro对数据库中的密码进行加密存储(java+springboot+shiro)
使用shiro对数据库中的密码进行加密存储(java+springboot+shiro)
127 0
|
1月前
|
存储 编解码 算法
ISO/IEC 23001:数字版权管理的加密标准(四)
ISO/IEC 23001:数字版权管理的加密标准(四)
56 0
|
1月前
|
存储 数据安全/隐私保护 数据格式
ISO/IEC 23001:数字版权管理的加密标准(二)
ISO/IEC 23001:数字版权管理的加密标准(二)
57 0
|
1月前
|
XML 算法 数据安全/隐私保护
Shiro -认证凭据(密码)加密的那些事
Shiro -认证凭据(密码)加密的那些事
46 0
|
存储 安全 算法
2021年你还不会Shiro?----4.使用MD5+盐+hash散列进行密码加密
上一篇文章里介绍了使用自定义的Realm来实现数据的获取,不过,数据的获取依然不是来源于真实的数据库或者是nosql,只是包装了一个方法,假装从这个方法里获取了数据库中的用户信息,然后我们返回了一个SimpleAccount,这个对象携带了用户名与密码,当时我们是明文返回的密码。这样做很显然是不安全的,一旦数据来源被攻破,所有的用户信息都会被泄露。所以这里我们介绍下,常用的密码加密策略。
161 0
2021年你还不会Shiro?----4.使用MD5+盐+hash散列进行密码加密
|
9月前
|
数据库 数据安全/隐私保护
Shiro学习-密码的比对及密码的MD5加密(八)
Shiro学习-密码的比对及密码的MD5加密(八)
75 0
|
数据安全/隐私保护
Shiro的Md5加密(带盐加密+Md5多次迭代)
Shiro的Md5加密(带盐加密+Md5多次迭代)
|
缓存 安全 网络安全
Shiro框架02权限认证+MD5加盐加密+散列1024+Hex/Base64(源码)
Shiro框架02权限认证+MD5加盐加密+散列1024+Hex/Base64(源码)
Shiro框架02权限认证+MD5加盐加密+散列1024+Hex/Base64(源码)