Shiro安全框架学习03 - 编码/加密

md5加密

在涉及密码存储问题上,应该进行加密存储,而不能是明文,否则账号密码泄露,就会产生意想不到的后果。所以,通常都会采用非对称加密,什么是非对称呢,就是不可逆的,而md5就是这样一个算法。
如123456用md5加密后,得到字符串:e10adc3949ba59abbe56e057f20f883e

String password = "123456";
String encodedPassword = new Md5Hash(password).toString();
System.out.println(encodedPassword);

上面讲了md5加密,但md5加密也有一些缺陷:如果我的密码是123456,你的也是123456,那么md5的值是一样的,通过比较加密后的字符串,就可以反推过来。
虽然md5不可逆,但可以用穷举法,把一些常用的密码的md5值记录下来,比如123,abcde等等,这样也可以破解一部分。
为了解决这个问题,引入了盐(salt)的概念,虽然每次123456的md5值都是e10adc3949ba59abbe56e057f20f883e,但是加上盐之后,即123456+随机数,那么md5值就不一样了,这个随机数 就是盐,而这个随机数也会在数据库里保存下来,每个不同的用户,随机数也是不一样的。再者就是加密次数,可以加密一次 也可以加密多次,加密次数越多,密文的安全性也就越高。

String password = "123456";
String salt = new SecureRandomNumberGenerator().nextBytes().toString(); //随机数:盐
int times = 2; //加密次数
String algorithmName = "md5"; //散列算法
 
String encodedPassword = new SimpleHash(algorithmName,password,salt,times).toString();
System.out.println("密文:" + encodedPassword);

获取密文和盐

public class PasswordHelper {

	public static void main(String[] args) {
		String algorithmName = "md5";
		String username = "itmyhome";
		String password = "123456";
		String salt1 = password;
		String salt2 = new SecureRandomNumberGenerator().nextBytes().toHex();
		int hashIterations = 2;
		SimpleHash hash = new SimpleHash(algorithmName, password,
				salt1 + salt2, hashIterations);
		String encodedPassword = hash.toHex();
		//每次输出结果都不一样
		System.out.println("密文:" + encodedPassword); //b4b88b2ef28089f9c802df11bd216a33
		System.out.println("盐:" + salt2); //713183f112f48824613f0f05d0d10060

	}

}

修改Realm

public class UserRealm extends AuthorizingRealm  {

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		// TODO Auto-generated method stub
		
		//获取账号密码
		UsernamePasswordToken t = (UsernamePasswordToken) token;
        String password = new String(t.getPassword());
		String username = (String) token.getPrincipal();
		
		//获取数据库种的密码、盐(此处写死,测试用)
		User user = new User();
		user.setUsername(username);
		user.setPassword("b4b88b2ef28089f9c802df11bd216a33");
		user.setSalt("713183f112f48824613f0f05d0d10060");
		
		if (!"itmyhome".equals(username)) {
			// 抛出 帐号找不到异常
			throw new UnknownAccountException();
		}
		
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
				user.getUsername(), // 用户名
				user.getPassword(), // 密码
				ByteSource.Util.bytes(password + user.getSalt()),// salt=password+salt
				getName() // realm name
		);
		return authenticationInfo;
	}

}

ini配置

[main]
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher  
credentialsMatcher.hashAlgorithmName=md5  
credentialsMatcher.hashIterations=2  
credentialsMatcher.storedCredentialsHexEncoded=true  

userRealm=com.itmyhome.UserRealm
securityManager.realms=$userRealm
#这行很重要
userRealm.credentialsMatcher=$credentialsMatcher

通过credentialsMatcher.hashAlgorithmName=md5指定散列算法为md5,需要和生成密码时的一样
credentialsMatcher.hashIterations=2,散列迭代次数
credentialsMatcher.storedCredentialsHexEncoded=true表示是否存储散列后的密码为16进制
此处最需要注意的就是HashedCredentialsMatcher的算法需要和生成密码时的算法一样。
另外HashedCredentialsMatcher会自动根据AuthenticationInfo的类型是否是SaltedAuthenticationInfo来获取credentialsSalt盐。

测试类

public class ShiroTest {

	private static Logger logger = LoggerFactory.getLogger(ShiroTest.class);

	public static void main(String[] args) {
      
		//获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager  
        Factory<SecurityManager> factory =  new IniSecurityManagerFactory("classpath:auth.ini");
 
        //得到SecurityManager实例 并绑定给SecurityUtils  
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        
        //得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)  
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("itmyhome","123456");
        
        try{
            //登录,即身份验证  
        	subject.login(token);
        }catch(org.apache.shiro.authc.AuthenticationException e){
        	//身份验证失败
        	e.printStackTrace();
        }
        
        System.out.println(subject.getPrincipal());
        System.out.println("User is authenticated:  " + subject.isAuthenticated());
    }
}
发布了691 篇原创文章 · 获赞 2948 · 访问量 409万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章