文章目录
Shiro凭证匹配器(密码匹配器)
1. 普通加密
加密:将明文加密成密文保存,常见的用户密码保存在数据库中是需要加密码保存的(防止出现密码泄露造成用户的损失)。
在实际项目中,密码基本上都是密文保存的(加密后保存)
常见加密算法:
MD5,SHA-512,SHA-256等等(或者叠加使用md5(SHA512))
常见加密解密算法地址:
http://www.ttmd5.com/list.php
Shiro普通加密(md5)实现
实现步骤:
1)对数据库密码进行md5加密
使用mysql的md5函数对密码进行加密,然后直接修改数据库的用户密码测试登录。
2) 在applicationContext-shiro.xml,添加加密认证配置
1.1 编写MD5加密工具类MD5Utils
/**MD5加密工具类
* 进行数据加密
* 本项目只用于测试,用shiro自带的hash.Md5Hash算法加密
*/
public class MD5Utils {
//参1 传入的明文
public static String stringToMD5(String plainText) {
byte[] secretBytes = null;
try {
//对明文的字节进行摘要加密
secretBytes = MessageDigest.getInstance("md5").digest(plainText.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("没有找到MD5算法");
}
//radix参数 16位或者32位(加密之后的位数)
String md5code = new BigInteger(1, secretBytes).toString(16);
//补0操作,一个字节转两位的16进制
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code = "0" + md5code;
}
return md5code;
}
}
1.2 测试MD5算法
/**
* 测试MD5加密工具类
* 测试shiro自带的hash.Md5Hash加密
*/
public class MD5UtilsTest {
//测试MD5工具类加密
@Test
public void stringToMD5() {
//对123456进行MD5加密
String result = MD5Utils.stringToMD5("123456");
//md5(123456)= e10adc3949ba59abbe56e057f20f883e
System.out.println(result);
}
//测试shiro自带的hash.Md5Hash算法加密,(shiro也集成常用的加密的算法md5,sha-1)
@Test
public void test02() {
// 对123456加密
Md5Hash md5Hash = new Md5Hash("123456");
System.out.println(md5Hash.toString()); //e10adc3949ba59abbe56e057f20f883e
}
}
1.3 在applicationContext-shiro.xml添加凭证匹配器(使用默认匹配器)
<!-- 配置一个密码的匹配器 -->
<!-- 123456 是用户提交过来的密码明文, 密码的匹配器,得到密文,与数据库的密码 -->
<!--创建凭证匹配器-->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!--指定加密算法-->
<property name="hashAlgorithmName" value="md5"/>
</bean>
<!-- 3.创建Realm(类似dao),在AuthRealm类上就不需要加@Component注解了 -->
<bean id="authRealm" class="com.xgf.web.shiro.AuthRealm">
<!-- 使用自定义匹配器(密码) -->
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
2. 加盐加密
1)为什么要加盐(加盐实现加密的意义)
如果只是单纯使用md5或sha1进行加密,容易被人利用"彩虹表"撞库来破解密码,导致密码不安全!这时可以进行加盐加密来解决。
破解方法:将很多的字符串进行调用加密算法,生成对应的很多的密文,然后根据密文倒过来查表(这个表非常的大比如100G,时间虽然久一点,但是能够破解),得出明文(撞库更具密文查找明文)。2)如何加盐加密
添加一个特殊字符串,别人推导明文的时候,还需要去推导那个特殊的字符串,增加推导任务,造成算率不够导致推导不出来等。
3)shiro加盐加密步骤
1、编写代码对密码加盐加密
2、编写自定义凭证匹配器
3、在applicationContext-shiro.xml,添加自定义凭证匹配器
2.1 测试加盐加密(添加一个特殊的字符串)
MD5UtilsTest
//测试加盐加密(添加一个特殊字符串)
@Test
public void test03() {
//参1:传入明文 参2:加盐(特殊字符串)
Md5Hash md5Hash = new Md5Hash("123456","strive_day@163.com");
System.out.println(md5Hash.toString());//2f660a3a69e023b9033c83ef3d418c9b
}
//测试加另一个盐(另一个字符串)
@Test
public void test04() {
Md5Hash md5Hash = new Md5Hash("123456","study@export.com");//参1 传入明文 参2盐
System.out.println(md5Hash.toString());//4154661a85ae8bafac91433e7003f4a2和上面的密文不一样,因为特殊字符串盐不一样
}
2.2 配置自定义的密码匹配器
CustomCredentialsMatcher
/**创建自己的密码匹配器 - 通过加盐加密对用户输入的明文进行加密然后和数据库中存储的密文比较
* 必须指定父类SimpleCredentialsMatcher
*/
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {
private Logger l = LoggerFactory.getLogger(this.getClass());
//将密码加密成密文,但需要使用账号(这里账号是邮箱)作盐
//subject.login(token) 获取页面提交的密码 (比如123456) 通过加盐加密转为 -> 密文1
//info 调relam 查询数据库中的密码(数据库中的密码存储就是加密存储的) -> 密文2
//通过比较密文1(用户输入的值)和密文2(数据库中的值),来判断用户输入是否正确
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
//token获取账号密码
UsernamePasswordToken t = (UsernamePasswordToken) token;
String email = t.getUsername(); //获取用户名(邮箱)
//密码是char[] '1','2','3' => "123" new String(char[])
String pwd1 = new String(t.getPassword()); //密码获取的是字符数组,转换为字符串
l.info("doCredentialsMatch 用户输入的密码明文pwd1 = "+pwd1);
//进行加盐加密,得到用户输入的密码明文加密后的密文
Md5Hash md5Hash = new Md5Hash(pwd1, email);//参1 传入明文 参2盐
pwd1 = md5Hash.toString(); //转换为字符串
l.info("doCredentialsMatch 用户输入的密码加盐加密后pwd1 "+pwd1);
//读取数据库的密码,数据库存储的就是密文
String pwd2 = (String) info.getCredentials();
l.info("doCredentialsMatch 数据库的密码密文pwd2 = "+pwd2);
if (pwd1.equals(pwd2)) {
return true;//密码正确(两个密文比较)
} else {
return false;//密码不正确
}
}
}
2.3 在applicationContext-shiro.xml添加自定义的凭证匹配器(自定义密码匹配)
<!--使用自己定义的匹配器-->
<bean id="credentialsMatcher2" class="com.xgf.web.utils.CustomCredentialsMatcher"></bean>
<!-- 3.创建Realm(类似dao),在AuthRealm类上就不需要加@Component注解了 -->
<bean id="authRealm" class="com.xgf.web.shiro.AuthRealm">
<!-- 使用自定义匹配器(密码) -->
<property name="credentialsMatcher" ref="credentialsMatcher2"/>
</bean>
2.4 修改业务,当添加一个用户的时候进行密码加盐加密保存
UserServiceImpl
@Override
public void saveUser(User user) {
String uuid= UUID.randomUUID().toString();
user.setUserId(uuid);
//原来保存用户使用的密码是明文,现在需要对它进行加密
if(user.getPassword()!=null){//不能对空字符串进行加密
//使用shiro自带的加密Md5Hash
//参1:密码明文 参2:盐(按照邮箱设置盐)
Md5Hash md5Hash = new Md5Hash(user.getPassword(),user.getEmail());
user.setPassword(md5Hash.toString());
}
l.info("save user = " + user);
iUserDao.save(user);
}
2.5 shiro退出登录
UserController
//使用shiro退出登录(不再使用前面的普通登录退出了)
@RequestMapping(path = "/loginOut-shiro", method = {RequestMethod.GET, RequestMethod.POST})
public String loginOutShiro(){
//删除session中的用户信息
Subject subject = SecurityUtils.getSubject();
//shiro退出登录
subject.logout();
l.info("loginOut-shiro注销用户");
return "redirect:/login-shiro.jsp";
}
将header.jsp
的注销方式改为shiro注销
<%-- 非加密方式注销 --%>
<%--<a href="${path}/system/user/loginOut" class="btn btn-default btn-flat">注销</a>--%>
<%--使用shiro退出 subject.logout(); --%>
<a href="${path}/system/user/loginOut-shiro" class="btn btn-default btn-flat">注销</a>
2.5 测试登录,登录成功(数据库密码已经加盐加密)