03-自定义Realm支持的MD5散列算法(ssm+Shiro)

简介:

  散列算法一般用于生成一段文本的摘要信息,散列算法不可逆,将内容可以生成摘要,无法将摘要转成原始内容。散列算法常用于对密码进行散列,常用的散列算法有MD5SHA。一般散列算法需要提供一个salt(盐)与原始内容生成摘要信息,这样做的目的是为了安全性,比如:111111md5值是:96e79218965eb72c92a549dd5a330112,拿着“96e79218965eb72c92a549dd5a330112”去md5破解网站很容易进行破解,如果要是对111111salt(盐,一个随机数)进行散列,这样虽然密码都是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

 

posted @ 2017-07-23 14:43  半生戎马,共话桑麻、  阅读(196)  评论(0)    收藏  举报
levels of contents