上一篇我们提到了使用Shiro为密码进行MD5加密,这次来说一下密码加盐的问题。
当两个用户的密码相同时,单纯使用不加盐的MD5加密方式,会发现数据库中存在相同结构的密码,
这样也是不安全的。我们希望即便是两个人的原始密码一样,加密后的结果也不一样。
如何做到呢?其实就好像炒菜一样,两道一样的鱼香肉丝,加的盐不一样,炒出来的味道就不一样。
MD5加密也是一样,需要进行盐值加密。

在之前的加密样例中,我们可以注意到SimpleHash构造方法的参数中有一个salt参数,该参数就
是MD5加密的盐值信息:

public static void main(String[] args) {String hashAlgorithmName = "MD5";//加密方式Object crdentials = "123456";//密码原值Object salt = null;//盐值int hashIterations = 1024;//加密1024次Object result = new SimpleHash(hashAlgorithmName,crdentials,salt,hashIterations);System.out.println(result);
}

加盐需要注意以下步骤:
(1)加密之后的结果要加上盐。
(2)返回值(AuthenticationInfo)要将盐带过去。

我们回顾一下之前的校验Realm:

package com.test.shiro.realms;
import java.util.HashMap;
import java.util.Map;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthenticatingRealm;
import com.test.shiro.po.User;public class ShiroRealm extends AuthenticatingRealm{private static Map<String,User> userMap = new HashMap<String,User>();static{//使用Map模拟数据库获取User表信息userMap.put("jack", new User("jack","aaa123",false));userMap.put("tom", new User("tom","bbb321",false));userMap.put("jean", new User("jean","ccc213",true));}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//1.把AuthenticationToken转换为UsernamePasswordTokenUsernamePasswordToken userToken = (UsernamePasswordToken) token;//2.从UsernamePasswordToken中获取usernameString username = userToken.getUsername();//3.调用数据库的方法,从数据库中查询Username对应的用户记录System.out.println("从数据看中获取UserName为"+username+"所对应的信息。");//Map模拟数据库取数据User u = userMap.get(username);//4.若用户不行存在,可以抛出UnknownAccountExceptionif(u==null){throw new UnknownAccountException("用户不存在");}//5.若用户被锁定,可以抛出LockedAccountExceptionif(u.isLocked()){throw new LockedAccountException("用户被锁定");}//7.根据用户的情况,来构建AuthenticationInfo对象,通常使用的实现类为SimpleAuthenticationInfo//以下信息是从数据库中获取的//1)principal:认证的实体信息,可以是username,也可以是数据库表对应的用户的实体对象Object principal = u.getUsername();//2)credentials:密码Object credentials = u.getPassword();//3)realmName:当前realm对象的name,调用父类的getName()方法即可String realmName = getName();SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,credentials,realmName);return info;}public static void main(String[] args) {String hashAlgorithmName = "MD5";//加密方式Object crdentials = "123456";//密码原值Object salt = null;//盐值int hashIterations = 1024;//加密1024次Object result = new SimpleHash(hashAlgorithmName,crdentials,salt,hashIterations);System.out.println(result);}
}

其中doGetAuthenticationInfo是用于返回相应账号对应的数据库中账号密码信息的方法,下面
的main是测试Shiro的MD5加密方法。

如果我们要对密码进行MD5加盐操作,我们的返回值就不能是SimpleAuthenticationInfo的简单构造
方法了,要使用最复杂的,带有盐值参数的构造方法:
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, hashedCredentials, credentialsSalt, realmName);
其中principal是账号信息,hashedCredentials是MD5加密后的密码,credentialsSalt是密码加密的
盐值,realmName为当前realm对象的name。

对于盐值credentialsSalt,在Shiro中为org.apache.shiro.util.ByteSource对象:
ByteSource credentialsSalt = ByteSource.Util.bytes(“”);
ByteSource提供了一个内部方法,可以将字符串转换为对应的盐值信息。一般情况下我们使用一个
唯一的字符串作为盐值。在本测试样例中,我们使用用户名作为盐的原始值。

在上面的测试样例中,模拟的账号密码信息如下:

private static Map<String,User> userMap = new HashMap<String,User>();
static{//使用Map模拟数据库获取User表信息userMap.put("jack", new User("jack","aaa123",false));userMap.put("tom", new User("tom","bbb321",false));userMap.put("jean", new User("jean","ccc213",true));
}

为了对应测试账号的加密后的密码,我们要将密码进行加盐加密,改写刚才的main对应的测试方法,
分别为jack、tom以及jean的账号加盐加密:

public static void main(String[] args) {User u = null;Iterator<String> it = userMap.keySet().iterator();while(it.hasNext()){u = userMap.get(it.next());String hashAlgorithmName = "MD5";//加密方式Object crdentials = u.getPassword();//密码原值ByteSource salt = ByteSource.Util.bytes(u.getUsername());//以账号作为盐值int hashIterations = 1024;//加密1024次Object result = new SimpleHash(hashAlgorithmName,crdentials,salt,hashIterations);System.out.println(u.getUsername()+":"+result);}
}

结果:

然后将这些密码设置到静态代码块中,初始化测试账号:

private static Map<String,User> userMap = new HashMap<String,User>();
static{//使用Map模拟数据库获取User表信息userMap.put("jack", new User("jack","43e66616f8730a08e4bf1663301327b1",false));userMap.put("tom", new User("tom","3abee8ced79e15b9b7ddd43b95f02f95",false));userMap.put("jean", new User("jean","1a287acb0d87baded1e79f4b4c0d4f3e",true));
}

然后在下面的doGetAuthenticationInfo方法中,改写之前封装账号密码的方式:

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//1.把AuthenticationToken转换为UsernamePasswordTokenUsernamePasswordToken userToken = (UsernamePasswordToken) token;//2.从UsernamePasswordToken中获取usernameString username = userToken.getUsername();//3.调用数据库的方法,从数据库中查询Username对应的用户记录System.out.println("从数据看中获取UserName为"+username+"所对应的信息。");//Map模拟数据库取数据User u = userMap.get(username);//4.若用户不行存在,可以抛出UnknownAccountExceptionif(u==null){throw new UnknownAccountException("用户不存在");}//5.若用户被锁定,可以抛出LockedAccountExceptionif(u.isLocked()){throw new LockedAccountException("用户被锁定");}//7.根据用户的情况,来构建AuthenticationInfo对象,通常使用的实现类为SimpleAuthenticationInfo//以下信息是从数据库中获取的//1)principal:认证的实体信息,可以是username,也可以是数据库表对应的用户的实体对象Object principal = u.getUsername();//2)credentials:密码Object credentials = u.getPassword();//3)realmName:当前realm对象的name,调用父类的getName()方法即可String realmName = getName();//4)credentialsSalt盐值ByteSource credentialsSalt = ByteSource.Util.bytes(principal);//使用账号作为盐值SimpleAuthenticationInfo info = null; //new SimpleAuthenticationInfo(principal,credentials,realmName);info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);return info;
}

可以看到,之前封装返回给校验类的SimpleAuthenticationInfo类一共是三步,分别是获取数据库中的
用户账号、密码以及当前realm对象的name,而现在多了一步创建credentialsSalt盐值,并且以账号作
为盐的原值,而SimpleAuthenticationInfo的构造方法也使用了带有盐值参数的构造方法。

完成上面的代码,MD5的盐值加密就成功了。下面我们来测试一下,启动我们的Shiro3测试工程:

进入登录界面:

在上面输入jack的账号以及密码“aaa123”,在后台的Controller的登录方法断点中,可以看到用户
输入的密码原值:

然后在校验ShiroRealm类中的doGetAuthenticationInfo方法,可以看到账号密码以及盐值信息:

然后发现校验成功,用户成功登录:

最后总结一下Shiro的MD5加盐加密:
1)在doGetAuthenticationInfo方法返回值创建SimpleAuthenticationInfo对象的时候,需要使用
SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName)构造器。
2)使用ByteSource.Util.bytes()来计算盐值
3)盐值需要唯一,一般使用随机字符串或者userid
4)使用new SimpleHash(hashAlgorithmName,crdentials,salt,hashIterations)来计算盐值加密
后的密码的值。
转载请注明出处:http://blog.csdn.net/acmman/article/details/78585662

【Shiro权限管理】10.Shiro为密码加盐相关推荐

  1. SSM集成shiro权限管理

    这几天在学习了shiro权限管理框架,在刚开始的时候学的时候因为这个配置问题困扰了我很长时间,所以在这篇文章我整合了自己用SSM搭建shiro权限框架的过程. 1.配置 1.1jar包 在项目配置开始 ...

  2. Spring Boot Shiro 权限管理

    Spring Boot Shiro 权限管理 标签: springshiro 2016-01-14 23:44 94587人阅读 评论(60) 收藏 举报 本来是打算接着写关于数据库方面,集成MyBa ...

  3. SpringMVC+Shiro权限管理

    SpringMVC+Shiro权限管理 什么是权限呢?举个简单的例子: 我有一个论坛,注册的用户分为normal用户,manager用户. 对论坛的帖子的操作有这些: 添加,删除,更新,查看,回复 我 ...

  4. Shiro 权限管理入门之认证与授权

    Shiro 权限管理 什么是权限管理? 什么是身份认证? 什么是授权? Shiro 是什么? Shiro 的核心架构 Shiro 中的认证 认证关键对象 认证流程 认证的开发 自定义 Realm Si ...

  5. 39 Spring Boot Shiro权限管理【从零开始学Spring Boot】

    [视频 & 交流平台] à SpringBoot视频 http://study.163.com/course/introduction.htm?courseId=1004329008& ...

  6. Shiro权限管理实现(详解)

    前言 Apache Shiro 是 Java 的一个安全框架.功能强大,使用简单的Java安全框架,它为开发人员提供一个直观而全面的认证,授权,加密及会话管理的解决方案. 功能介绍 资源-角色-权限 ...

  7. shiro权限管理基本原理和实现的整理

    shiro权限管理基本原理和实现的整理 引言:这两天学习了一个对权限管理的新的框架shiro,在这里做一个总结,既为了帮助有需要的人,也方便自己以后来回顾. 本篇文章主要针对下面几个关键点来说明: 1 ...

  8. Springboot之Shiro权限管理

    文章目录 Springboot之Shiro权限管理 介绍 1.shiro有三个核心api(接口) 2.如何使用: 第一步导入shiro与springboot整合依赖 第二步配置shiro类 第三步自定 ...

  9. shiro权限管理的框架、加密、授权

    shiro权限管理的框架 1.权限管理的概念 ​ 基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且 ...

最新文章

  1. mysql init file_关于MySQL的init-file选项的用法实例
  2. 8、D8: Default interface methods are only supported starting with Android N (--min-api 24): void
  3. 【概率论与数理统计】假设检验
  4. asm.js的陷阱1
  5. caffe的prototxt文件
  6. XML--视频--人脸VOC
  7. linux启动keepalived服务,keepalived的原理及安装应用
  8. 魔法函数%matplotlib 解决matplotlib画图在Jupter/IPython中不显示
  9. Diango博客--10.交流的桥梁“评论功能”
  10. 设计模式笔记二十二:空对象模式
  11. 矩阵分析 第二章 lambda矩阵和Jordan标准型
  12. Linux IP别名,接口绑定,多网卡绑定
  13. Linux 系统如何更改主机名
  14. 头歌平台(EduCoder)—— 数据挖掘算法原理与实践:数据预处理
  15. 使用JDBC创建出版社和书籍管理系统
  16. office2010在安装过程中出错Error 1935的解决方法
  17. Linux(Ubuntu 22.04)虚拟机共享主机上的文件夹
  18. 一梦江湖网页提交问题服务器错误,一梦江湖4月3日更新内容详情一览
  19. 如何在把微信公众号生成链接
  20. Fabric 009 NodeJs Express安装与运行记录

热门文章

  1. csirs参考信号_一种信道状态信息参考信号CSI-RS的发送方法、装置及基站_2015109520063_说明书_专利查询_专利网_钻瓜专利网...
  2. Mac 上使用 SAS 的 2 种方法
  3. docker是干什么的,docker常用命令每日一练
  4. 常见的黑客入侵手段有哪些?
  5. QQ、微信、新浪 利用refresh_token重新登录
  6. 彻底删除的文件如何恢复?误删数据恢复,四种方法就可以解决
  7. 通过一个网络错误简述DNS
  8. Java小型项目:购物车小程序
  9. html自动淡入淡出,纯css实现淡入淡出_html/css_WEB-ITnose
  10. 编译原理—实验二LL(1)语法分析(一)