03-自定义Realm支持的MD5散列算法(ssm+Shiro)
简介:
散列算法一般用于生成一段文本的摘要信息,散列算法不可逆,将内容可以生成摘要,无法将摘要转成原始内容。散列算法常用于对密码进行散列,常用的散列算法有MD5、SHA。一般散列算法需要提供一个salt(盐)与原始内容生成摘要信息,这样做的目的是为了安全性,比如:111111的md5值是:96e79218965eb72c92a549dd5a330112,拿着“96e79218965eb72c92a549dd5a330112”去md5破解网站很容易进行破解,如果要是对111111和salt(盐,一个随机数)进行散列,这样虽然密码都是111111加不同的盐会生成不同的散列值。
实例:
package cn.itcast.shiro.authentication; import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.crypto.hash.SimpleHash; public class MD5Test { public static void main(String[] args) { // 原始密码 String password = "111111"; System.out.println("原始密码:" + password); System.out.println("--------------------------------"); // 盐 String salt = "hello"; // 散列次数 int count_one = 1; int count_two = 2; // MD5散列算法加密 Md5Hash md5Hash_one = new Md5Hash(password, salt, count_one); Md5Hash md5Hash_two = new Md5Hash(password, salt, count_two); String password_md5_one = md5Hash_one.toString(); String password_md5_two = md5Hash_two.toString(); // 打印散列结果 // 散列1次:4ab06e04b551df5f06878a1713a7a090 System.out.println("经过MD5散列算法【散列1次】加密后的密码:" + password_md5_one); // 散列2次:7b76b0fbaa69fa2fc77359dc3e30302c System.out.println("经过MD5散列算法【散列2次】加密后的密码:" + password_md5_two); System.out.println("--------------------------------"); // 使用SimpleHash进行散列(同MD5) // 第一个参数:算法的名称;第二个参数:原始密码;第三个参数:盐;第四个参数:散列次数 String algorithmName = "md5"; SimpleHash simpleHash_one = new SimpleHash(algorithmName, password, salt, count_one); SimpleHash simpleHash_two = new SimpleHash(algorithmName, password, salt, count_two); System.out.println("SimpleHash方式【散列1次】:" + simpleHash_one.toString()); System.out.println("SimpleHash方式【散列2次】:" + simpleHash_two.toString()); } }
打印结果:
原始密码:111111
--------------------------------
经过MD5散列算法【散列1次】加密后的密码:4ab06e04b551df5f06878a1713a7a090
经过MD5散列算法【散列2次】加密后的密码:7b76b0fbaa69fa2fc77359dc3e30302c
--------------------------------
SimpleHash方式【散列1次】:4ab06e04b551df5f06878a1713a7a090
SimpleHash方式【散列2次】:7b76b0fbaa69fa2fc77359dc3e30302c
-------------------------------------------------------------------------------------------------
自定义的Realm支持MD5散列算法:
实际应用是将盐和散列后的值存在数据库中,自动realm从数据库取出盐和加密后的值由shiro完成密码校验。
一、支持MD5散列的ini配置文件shiro-realm-MD5.ini
[main] # 定义凭证匹配器 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher # 散列算法名称 credentialsMatcher.hashAlgorithmName=md5 # 散列次数 credentialsMatcher.hashIterations=1 # 将凭证匹配器设置到realm customRealm=cn.itcast.shiro.realm.CustomRealm_MD5 customRealm.credentialsMatcher=$credentialsMatcher # 将自定义的Realm注入到SecurityManager中,这里相当于spring中的注入 securityManager.realms=$customRealm
二、自定义Realm修改:CustomRealm_MD5.java
package cn.itcast.shiro.realm; 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.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; // 自定义Realm public class CustomRealm_MD5 extends AuthorizingRealm { /** * 认证 * token:用户输入的令牌,可以是字符串,也可以是对象 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 从token中获取用户名 String username = (String) token.getPrincipal(); // 根据用户输入的用户名从数据库中查找用户信息(密码),这里只是模拟的静态数据 // 如果查不到就返回null... // 假设数据库中的用户名是PeterDB if (!"Peter".equals(username)) { return null; // 返回null之后,下面的代码都不执行 } // 如果查到就返回password,MD5散列1次后的密码(原始密码111111),比较的是散列后的密码 String password_MD5 = "4ab06e04b551df5f06878a1713a7a090"; String salt = "hello"; // 返回认证信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password_MD5, ByteSource.Util.bytes(salt), this.getName()); return simpleAuthenticationInfo; } // Realm的名称(自定义),可以不定义 @Override public void setName(String name) { super.setName("customRealm_MD5"); } /** * 授权 * principals: */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return null; } }
四、测试代码:
package cn.itcast.shiro.authentication; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; 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 org.junit.Test; // 认证测试 public class AuthenticationTest { // 自定义Realm @Test public void testCustomRealm() { // 创建SecurityManager工厂 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm-MD5.ini"); // 获取SecurityManager SecurityManager securityManager = factory.getInstance(); // 将securityManager设置到运行环境中 SecurityUtils.setSecurityManager(securityManager); // 创建Subject,这个就是从数据库中获取的正确的主体,包含用户名和凭证(如密码、数字签名、指纹等等) Subject subject = SecurityUtils.getSubject(); // 创建Token,这个token就是用户输入的用户名和密码做成的token令牌,将来要从数据库中获取 UsernamePasswordToken token = new UsernamePasswordToken("Peter", "111111"); // 认证提交,认证不通过时会报异常 try { subject.login(token); } catch (AuthenticationException e) { e.printStackTrace(); } // 认证结果:认证通过放回true,失败返回false boolean isAuthenticated = subject.isAuthenticated(); // 打印认证结果 System.out.println("认证结果:" + isAuthenticated); } }
工程结构:(没有的文件是之前留下来的,只写上面要求的几个文件即可)
打印结果:认证结果:true