数据加密的基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码,通常称为"密文",使其只能在输入相应的密钥之后才能显示出本来内容,通过这样的途径来达到保护数据不被非法人窃取、阅读的目的。该过程的逆过程为解密,即将该编码信息转化为其原来数据的过程。
例如,当用户注册账号之后,输入的密码不能是单纯的用户输入的数据存储在数据库中,我们需要对其加密,将加密之后的的数据存在数据库,这样才能保证密码的安全性
加密的分类
加密有两种,一种是对称加密,另外一种是非对称加密,这里简单做下介绍
对称加密
双方使用的同一个密钥,既可以加密又可以解密,这种加密方法称为对称加密,也称为单密钥加密。
非对称加密
一对密钥由公钥和私钥组成(可以使用很多对密钥)。私钥解密公钥加密数据,公钥解密私钥加密数据(私钥公钥可以互相加密解密)。
加密算法
单向加密
单向加密是不可逆的,也就是只能加密,不能解密。通常用来传输类似用户名和密码,直接将加密后的数据提交到后台,因为后台不需要知道用户名和密码,可以直接将收到的加密后的数据存储到数据库
双向加密
通常分为对称性加密算法和非对称性加密算法,对于对称性加密算法,信息接收双方都需事先知道密匙和加解密算法且其密匙是相同的,之后便是对数据进行加解密了。非对称算法与之不同,发送双方A,B事先均生成一堆密匙,然后A将自己的公有密匙发送给B,B将自己的公有密匙发送给A,如果A要给B发送消 息,则先需要用B的公有密匙进行消息加密,然后发送给B端,此时B端再用自己的私有密匙进行消息解密,B向A发送消息时为同样的道理。
常见的加密算法
在实际开发中,用的最多的为MD5和SHA1加密算法了
下面来看看MD5加密算法
MD5源码
* Licensed to the Apache Software Foundation (ASF) under one
package org.apache.shiro.crypto.hash;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.Hex;
/**
* Generates an MD5 Hash (RFC 1321) from a given input <tt>source</tt> with an optional <tt>salt</tt> and
* hash iterations.
* <p/>
* See the {@link SimpleHash SimpleHash} parent class JavaDoc for a detailed explanation of Hashing
* techniques and how the overloaded constructors function.
*
* @since 0.9
*/
public class Md5Hash extends SimpleHash {
//TODO - complete JavaDoc
public static final String ALGORITHM_NAME = "MD5";
public Md5Hash() {
super(ALGORITHM_NAME);
}
public Md5Hash(Object source) {
super(ALGORITHM_NAME, source);
}
public Md5Hash(Object source, Object salt) {
super(ALGORITHM_NAME, source, salt);
}
public Md5Hash(Object source, Object salt, int hashIterations) {
super(ALGORITHM_NAME, source, salt, hashIterations);
}
public static Md5Hash fromHexString(String hex) {
Md5Hash hash = new Md5Hash();
hash.setBytes(Hex.decode(hex));
return hash;
}
public static Md5Hash fromBase64String(String base64) {
Md5Hash hash = new Md5Hash();
hash.setBytes(Base64.decode(base64));
return hash;
}
}
我们可以发现在MD5中提供了几个几个构造方法,我们可以通过传递参数来对数据进行加密
package com.zhouym.junit;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.junit.Test;
public class JunitTest {
@Test
public void test() {
//对数据进行加密,参数为要加密的数据
Md5Hash md1 = new Md5Hash("123456");
System.out.println(md1);
//参数需加密的数据,以及盐值salt,就是往加密数据中添加一点东西,就像加盐一样
Md5Hash md2 = new Md5Hash("123456","abc");
System.out.println(md2);
//参数为:加密数据+盐值+迭代此数,迭代次数以为,在第一次加密后的结果上把盐值再加一遍
Md5Hash md3 = new Md5Hash("123456", "abc", 2);
System.out.println(md3);
}
}
测试结果
盐值的作用
使用MD5存在一个问题,相同的password生成的hash值是相同的,如果两个用户设置了相同的密码,那么数据库中会存储两个相同的值,这是极不安全的,加Salt可以在一定程度上解决这一问题,所谓的加Salt方法,就是加点‘佐料’。其基本想法是这样的,当用户首次提供密码时(通常是注册时)由系统自动往这个密码里撒一些‘佐料’,然后在散列,而当用户登录时,系统为用户提供的代码上撒上相同的‘佐料’,然后散列,再比较散列值,来确定密码是否正确。
在shiro中使用MD5加密
认证方法修改
package com.zhouym.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.SimpleByteSource;
/**
* @author Administrator
*
*/
public class MyRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String principal = (String)token.getPrincipal();
if (!"zhouym".equals(principal)) {
return null;
}
//密码为12345,salt值为abc,散列此数为1,生成加密后的数据
String password = "d6b0ab7f1c8ab8f514db9a6d85de160a";
AuthenticationInfo info = new SimpleAuthenticationInfo(principal, password, new SimpleByteSource("abc"),"myrealm");
return info;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
}
shiro.ini文件
[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=1
#将凭证匹配器设置到realm
customRealm=com.zhouym.realm.MyRealm
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm
package com.zhouym.junit;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Test;
public class JunitTest {
@Test
public void test() {
//参数解释:明文+盐值+散列
//Md5Hash md5 = new Md5Hash("12345","abc",1);
//System.out.println(md5);
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
AuthenticationToken token = new UsernamePasswordToken("zhouym", "12345");
try {
subject.login(token);
System.out.println("登录成功");
} catch (UnknownAccountException e) {
System.out.println("账号或密码错误");
}catch (IncorrectCredentialsException e) {
System.out.println("账号或密码错误");
}
}
}
测试结果