【1.4】新建ClientTest
1. package com.itheima.shiro.client; 2. 3. import com.itheima.shiro.tools.EncodesUtil; 4. import org.junit.Test; 5. 6. /** 7. * @Description:测试 8. */ 9. public class ClientTest { 10. 11. /** 12. * @Description 测试16进制编码 13. */ 14. @Test 15. public void testHex(){ 16. String val = "holle"; 17. String flag = EncodesUtil.encodeHex(val.getBytes()); 18. String valHandler = new String(EncodesUtil.decodeHex(flag)); 19. System.out.println("比较结果:"+val.equals(valHandler)); 20. } 21. 22. /** 23. * @Description 测试base64编码 24. */ 25. @Test 26. public void testBase64(){ 27. String val = "holle"; 28. String flag = EncodesUtil.encodeBase64(val.getBytes()); 29. String valHandler = new String(EncodesUtil.decodeBase64(flag)); 30. System.out.println("比较结果:"+val.equals(valHandler)); 31. } 32. 33. 34. }
【1.5】小结
1. 1、shiro目前支持的编码与解码: 2. base64 3. (HEX)16进制字符串 4. 2、那么shiro的编码与解码什么时候使用呢?又是怎么使用的呢?
【2】散列算法
散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,常见的散列算法如MD5、SHA等。一般进行散列时最好提供一个salt(盐),比如加密密码“admin”,产生的散列值是“21232f297a57a5a743894a0e4a801fc3”,可以到一些md5解密网站很容易的通过散列值得到密码“admin”,即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,如salt(即盐);这样散列的对象是“密码+salt”,这样生成的散列值相对来说更难破解。
shiro支持的散列算法:
Md2Hash、Md5Hash、Sha1Hash、Sha256Hash、Sha384Hash、Sha512Hash
【2.1】新增DigestsUtil
1. package com.itheima.shiro.tools; 2. 3. import com.sun.org.apache.bcel.internal.generic.NEW; 4. import org.apache.shiro.crypto.SecureRandomNumberGenerator; 5. import org.apache.shiro.crypto.hash.SimpleHash; 6. import sun.security.util.Password; 7. 8. import java.util.HashMap; 9. import java.util.Map; 10. 11. /** 12. * @Description:摘要 13. */ 14. public class DigestsUtil { 15. 16. private static final String SHA1 = "SHA-1"; 17. 18. private static final Integer ITERATIONS =512; 19. 20. /** 21. * @Description sha1方法 22. * @param input 需要散列字符串 23. * @param salt 盐字符串 24. * @return 25. */ 26. public static String sha1(String input, String salt) { 27. return new SimpleHash(SHA1, input, salt,ITERATIONS).toString(); 28. } 29. 30. /** 31. * @Description 随机获得salt字符串 32. * @return 33. */ 34. public static String generateSalt(){ 35. SecureRandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator(); 36. return randomNumberGenerator.nextBytes().toHex(); 37. } 38. 39. 40. /** 41. * @Description 生成密码字符密文和salt密文 42. * @param 43. * @return 44. */ 45. public static Map<String,String> entryptPassword(String passwordPlain) { 46. Map<String,String> map = new HashMap<>(); 47. String salt = generateSalt(); 48. String password =sha1(passwordPlain,salt); 49. map.put("salt", salt); 50. map.put("password", password); 51. return map; 52. } 53. }
【2.2】新增ClientTest
1. package com.itheima.shiro.client; 2. 3. import com.itheima.shiro.tools.DigestsUtil; 4. import com.itheima.shiro.tools.EncodesUtil; 5. import org.junit.Test; 6. 7. import java.util.Map; 8. 9. /** 10. * @Description:测试 11. */ 12. public class ClientTest { 13. 14. /** 15. * @Description 测试16进制编码 16. */ 17. @Test 18. public void testHex(){ 19. String val = "holle"; 20. String flag = EncodesUtil.encodeHex(val.getBytes()); 21. String valHandler = new String(EncodesUtil.decodeHex(flag)); 22. System.out.println("比较结果:"+val.equals(valHandler)); 23. } 24. 25. /** 26. * @Description 测试base64编码 27. */ 28. @Test 29. public void testBase64(){ 30. String val = "holle"; 31. String flag = EncodesUtil.encodeBase64(val.getBytes()); 32. String valHandler = new String(EncodesUtil.decodeBase64(flag)); 33. System.out.println("比较结果:"+val.equals(valHandler)); 34. } 35. 36. @Test 37. public void testDigestsUtil(){ 38. Map<String,String> map = DigestsUtil.entryptPassword("123"); 39. System.out.println("获得结果:"+map.toString()); 40. } 41. 42. }
4、Realm使用散列算法
上面我们了解编码,以及散列算法,那么在realm中怎么使用?在shiro-day01-02realm中我们使用的密码是明文的校验方式,也就是SecurityServiceImpl中findPasswordByLoginName返回的是明文123的密码
1. package com.itheima.shiro.service.impl; 2. 3. import com.itheima.shiro.service.SecurityService; 4. 5. /** 6. * @Description:权限服务层 7. */ 8. public class SecurityServiceImpl implements SecurityService { 9. 10. @Override 11. public String findPasswordByLoginName(String loginName) { 12. return "123"; 13. } 14. }
【1】新建项目
shiro-day01-05-ciphertext-realm
【2】创建密文密码
使用ClientTest的testDigestsUtil创建密码为“123”的password密文和salt密文
1. password:56265d624e484ca62c6dfbc523e6d6fc7932d0d5 2. salt:845a66ac80174c0e486db9354cf84f9a
【3】修改SecurityService
SecurityService修改成返回salt和password的map
1. package com.itheima.shiro.service; 2. 3. import java.util.Map; 4. 5. /** 6. * @Description:权限服务接口 7. */ 8. public interface SecurityService { 9. 10. /** 11. * @Description 查找密码按用户登录名 12. * @param loginName 登录名称 13. * @return 14. */ 15. Map<String,String> findPasswordByLoginName(String loginName); 16. }
1. package com.itheima.shiro.service.impl; 2. 3. import com.itheima.shiro.service.SecurityService; 4. 5. import java.util.HashMap; 6. import java.util.Map; 7. 8. /** 9. * @Description:权限服务层 10. */ 11. public class SecurityServiceImpl implements SecurityService { 12. 13. @Override 14. public Map<String,String> findPasswordByLoginName(String loginName) { 15. //模拟数据库中存储的密文信息 16. return DigestsUtil.entryptPassword("123"); 17. } 18. }
【4】指定密码匹配方式
为DefinitionRealm类添加构造方法如下:
1. /** 2. * @Description 构造函数 3. */ 4. public DefinitionRealm() { 5. //指定密码匹配方式为sha1 6. HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(DigestsUtil.SHA1); 7. //指定密码迭代次数 8. matcher.setHashIterations(DigestsUtil.ITERATIONS); 9. //使用父亲方法使匹配方式生效 10. setCredentialsMatcher(matcher); 11. }
修改DefinitionRealm类的认证doGetAuthenticationInfo方法如下
1. /** 2. * @Description 认证接口 3. * @param token 传递登录token 4. * @return 5. */ 6. @Override 7. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 8. //从AuthenticationToken中获得登录名称 9. String loginName = (String) token.getPrincipal(); 10. SecurityService securityService = new SecurityServiceImpl(); 11. Map<String, String> map = securityService.findPasswordByLoginName(loginName); 12. if (map.isEmpty()){ 13. throw new UnknownAccountException("账户不存在"); 14. } 15. String salt = map.get("salt"); 16. String password = map.get("password"); 17. //传递账号和密码:参数1:缓存对象,参数2:明文密码,参数三:字节salt,参数4:当前DefinitionRealm名称 18. return new SimpleAuthenticationInfo(loginName,password, ByteSource.Util.bytes(salt),getName()); 19. }
【5】测试