【散列算法概述】
用于生成数据的摘要信息,不可逆算法,用于存储密码或者密文数据。
常见散列算法类型:MD5、SHA
一般进行散列时提供一个”盐“,即系统知道的”干扰数据“
这样生成的密文更加难以被破解
【盐?】
加盐加密是一种对系统登录口令的加密方式,它实现的方式是将每一个口令跟一个n位随机数相关联,这个n位随机数叫做”盐“(salt)。
加盐加密是一种对系统登录口令的加密方式,它实现的方式是将每一个口令同一个叫做”盐“(salt)的n位随机数相关联。
无论何时只要口令改变,随机数就改变。
随机数以未加密的方式存放在口令文件中,这样每个人都可以读。
不再只保存加密过的口令,而是先将口令和随机数连接起来然后一同加密,加密后的结果放在口令文件中。
演示案例:
MD5加密
@Test public void calcTest() { String source = "123456"; Md5Hash hash1 = new Md5Hash(source); System.out.println("使用MD5加密后的结果:"+hash1.toString()); Md5Hash hash2 = new Md5Hash(source, "北京武汉"); System.out.println("使用MD5加密并加盐后的结果:"+hash2.toString()); Md5Hash hash3 = new Md5Hash(source, "北京武汉", 2); // 一般两次就够了 System.out.println("使用MD5加密加盐并散列两次后的结果:"+hash3.toString()); }
结果:
使用MD5加密后的结果:e10adc3949ba59abbe56e057f20f883e
使用MD5加密并加盐后的结果:19a45a53e8924a742f77183b8878a5fb
使用MD5加密加盐并散列两次后的结果:b4f48723743cc5bfa7c1716296703ce1
Process finished with exit code 0
封装的加密工具类:
public class EncryptionUtil { private EncryptionUtil(){} /** * MD5加密 * 对秘密明文加密 * @param plaintext 密码明文 * @param salt ”盐“ * @param hashFrequency 散列次数 * @return 密文 */ public static String md5Encryption(String plaintext, Object salt, Integer hashFrequency) { return new Md5Hash(plaintext, salt, hashFrequency).toString(); } /** * SHA1加密 * @param plaintext 密码明文 * @param salt ”盐“ * @param hashFrequency 散列次数 * @return 密文 */ public static String sha1Encryption(String plaintext, Object salt, Integer hashFrequency) { return new Sha1Hash(plaintext, salt, hashFrequency).toString(); } }
【凭证加密配置】
方案一,在自定义Realm类的构造器中实现:
public UserRealm() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5"); // 指定加密算法名称 hashedCredentialsMatcher.setHashIterations(2); // 指定散列次数 this.setCredentialsMatcher(hashedCredentialsMatcher); }
方案二,在shiro.ini文件中配置:
[main] # 配置凭证匹配器 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher credentialsMatcher.hashAlgorithmName=md5 credentialsMatcher.hashIterations=2 # 自定义Realm对象 userRealm = cn.echo42.shiro.realm.userRealm userRealm.credentialsMatcher = $credentialsMatcher # 安全管理器 securityManager=org.apache.shiro.mgt.DefaultSecurityManager securityManager.realm=$userRealm
方法三,在业务程序中配置:
就是把方案一放在外面处理了
@Test public void testSample() { log.info("My First Apache Shiro Application"); DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); UserRealm userRealm = new UserRealm(); HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5"); hashedCredentialsMatcher.setHashIterations(2); userRealm.setCredentialsMatcher(hashedCredentialsMatcher); defaultSecurityManager.setRealm(userRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); AuthenticationToken userToken = new UsernamePasswordToken(username,password); subject.login(userToken); }
其次,对应的也需要更改认证信息对象(UserRealm):
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { // 通过提交的令牌获取信息(用户名称?) String principal = authenticationToken.getPrincipal().toString(); // 或者是业务层调取记录对象用来验证 User user=userService.queryUserByUserName(username); // 如果这里从数据库获取的用户名核对成功 返回一个简单的验证结果对象 // 用户名,令牌凭证(就是密码),和这个类的限定名 if ("username".equals(principal)) // return new SimpleAuthenticationInfo(principal, authenticationToken.getCredentials(), this.getName()); return new SimpleAuthenticationInfo( principal, // 任意对象 authenticationToken.getCredentials(), // 从数据库返回的密文密码 ByteSource.Util.bytes("使用的盐,这里可以动态也可以是这种静态的"), getName() // 类名 ); // 否则返回空,表明查不到 return null; }