shiro之编码加密

前言

在涉及数据在网络上的传输时,都要将明文数据加密传输,并且通常情况下的加密都是不可逆的

编码与解码

shiro内部提供了base64和16进制字符串编码/解码的API支持,如下
base64编码/解码

1
2
3
4
5
6
String str="123456";
String base64Encoded=Base64.encodeToString(str.getBytes());//加密
System.out.println(base64Encoded);
String str2=Base64.decodeToString(base64Encoded); //解密
System.out.println(str2);
System.out.println(str.equals(str2));

输出结果:

MTIzNDU2
123456
true

16进制字符串编码/解码

1
2
3
4
5
6
String str="123456";
String base64Encoded = Hex.encodeToString(str.getBytes());//加密
System.out.println(base64Encoded);
String str2 = new String(Hex.decode(base64Encoded.getBytes())); //解密
System.out.println(str2);
System.out.println(str.equals(str2));

输出结果:

313233343536
123456
true

byte与string编码/解码

1
2
3
4
5
String str="123456";
byte[] str3=CodecSupport.toBytes(str, "utf-8"); //变成byte数组
System.out.println(str3);
String str4=CodecSupport.toString(str3, "utf-8"); //编程String
System.out.println(str4);

输出结果

[B@cd51c3
123456

散列算法

该算法是一种不可逆的算法,如MD5,SHA等,一般该算法最好需要提供一个salt(盐)来增加破解难度,一般情况下的盐被设置为 密码+用户名+盐,这样只有系统知道盐值,更难被破解

MD5散列

1
2
3
4
String str = "hello";
String salt = "world";
String md5 = new Md5Hash(str, salt,2).toString();
System.out.println(md5);

这里的2是散列次数,即迭代多少次,这里是2次

556b1a232d4d63a2f3ab87c9535abe25

其它散列

1
2
3
4
5
6
7
8
String str = "hello";
String salt = "world";
String sha1 = new Sha256Hash(str, salt).toString();
String SHA1=new Sha1Hash(str, salt).toString();
String SHA512=new Sha512Hash(str, salt).toString();
System.out.println(sha1);
System.out.println(SHA1);
System.out.println(SHA512);

相应的输入结果:

5715790a892990382d98858c4aa38d0617151575
3e64afa1cb7d643aa36f63b8d092ad76b1f04ff557abbb3d05f5b9037abf68a6606a8885d51bec8f6f39ee7d0badd504241c3704e777a51c21a9723e285fb9b8

通用散列

1
String simpleHash = new SimpleHash("SHA-1", str, salt).toString();

其中第一个参数指定采用哪种算法
输出结果:

5715790a892990382d98858c4aa38d0617151575

默认匹配
Shiro提供了HashService,默认提供了DefaultHashService实现。
先来看下它的构造函数

1
2
3
4
5
6
7
public DefaultHashService() {
this.algorithmName = "SHA-512";//默认算法SHA-512
this.iterations = 1; //生成Hash值的迭代次数,默认1
this.generatePublicSalt = false; //是否生成公盐,默认false
this.rng = new SecureRandomNumberGenerator();//用于生成公盐
}
String hex = randomNumberGenerator.nextBytes().toHex();//随机生成一个盐

加密与解密

Shiro还提供对称式加密/解密算法的支持,如AES、Blowfish等
AES算法

1
2
3
4
5
6
7
8
9
10
11
12
AesCipherService aesCipherService = new AesCipherService();
aesCipherService.setKeySize(128);//设置key长度
//生成key
Key key = aesCipherService.generateNewKey();
String text = "zwl";
//加密
String encrptText = aesCipherService.encrypt(text.getBytes(), key.getEncoded()).toHex();
//解密
String text2 = new String(aesCipherService.decrypt(Hex.decode(encrptText), key.getEncoded()).getBytes());
System.out.println(key.toString());
System.out.println(encrptText);
System.out.println(text2);

而上面的key就相当于一把钥匙,只有了这把钥匙,就能解密
输出结果:

javax.crypto.spec.SecretKeySpec@176a4
a316645482dff96d9c86c64adabc51dee5170232365a3c15dab941dc6b73c2ec
zwl

Blowfish算法

1
2
3
4
5
6
7
8
9
10
11
12
BlowfishCipherService blowfishCipherService = new BlowfishCipherService();
blowfishCipherService.setKeySize(128);
//生成key
Key key = blowfishCipherService.generateNewKey();
String text = "zwl";
//加密
String encrptText = blowfishCipherService.encrypt(text.getBytes(), key.getEncoded()).toHex();
//解密
String text2 = new String(blowfishCipherService.decrypt(Hex.decode(encrptText), key.getEncoded()).getBytes());
System.out.println(key.toString());
System.out.println(encrptText);
System.out.println(text2);

输出结果:

javax.crypto.spec.SecretKeySpec@d97afa5c
8e7f6626884b7b429a1000286f4990a4
zwl

PasswordService/CredentialsMatcher

在用户输入明文密码时,shiro默认的实现方式如下

1
2
3
4
5
6
7
8
9
public interface PasswordService {
//输入明文密码得到密文密码
String encryptPassword(Object plaintextPassword) throws IllegalArgumentException;
}
public interface CredentialsMatcher {
//匹配用户输入的token的凭证(未加密)与系统提供的凭证(已加密)
boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info);
}

Shiro默认提供了PasswordService实现DefaultPasswordService;CredentialsMatcher实现PasswordMatcher及HashedCredentialsMatcher

关于shiro的编码\加密是在是丰富多多彩,一般是学习中遇到采取查询相关的

一个对称加密的算法

下面是在一个项目中使用到的工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
public class DesUtils {
/** 字符串默认键值 */
private static String strDefaultKey = "national";
/** 加密工具 */
private Cipher encryptCipher = null;
/** 解密工具 */
private Cipher decryptCipher = null;
/**
* 将byte数组转换为表示16进制值的字符串, 如:byte[]{8,18}转换为:0813, 和public static byte[]
* hexStr2ByteArr(String strIn) 互为可逆的转换过程
*
* @param arrB
* 需要转换的byte数组
* @return 转换后的字符串
* @throws Exception
* 本方法不处理任何异常,所有异常全部抛出
*/
public static String byteArr2HexStr(byte[] arrB) throws Exception {
int iLen = arrB.length;
// 每个byte用两个字符才能表示,所以字符串的长度是数组长度的两倍
StringBuffer sb = new StringBuffer(iLen * 2);
for (int i = 0; i < iLen; i++) {
int intTmp = arrB[i];
// 把负数转换为正数
while (intTmp < 0) {
intTmp = intTmp + 256;
}
// 小于0F的数需要在前面补0
if (intTmp < 16) {
sb.append("0");
}
sb.append(Integer.toString(intTmp, 16));
}
return sb.toString();
}
/**
* 将表示16进制值的字符串转换为byte数组, 和public static String byteArr2HexStr(byte[] arrB)
* 互为可逆的转换过程
*
* @param strIn
* 需要转换的字符串
* @return 转换后的byte数组
* @throws Exception
* 本方法不处理任何异常,所有异常全部抛出
*
*/
public static byte[] hexStr2ByteArr(String strIn) throws Exception {
byte[] arrB = strIn.getBytes();
int iLen = arrB.length;
// 两个字符表示一个字节,所以字节数组长度是字符串长度除以2
byte[] arrOut = new byte[iLen / 2];
for (int i = 0; i < iLen; i = i + 2) {
String strTmp = new String(arrB, i, 2);
arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
}
return arrOut;
}
/**
* 默认构造方法,使用默认密钥
*
* @throws Exception
*/
public DesUtils() throws Exception {
this(strDefaultKey);
}
/**
* 指定密钥构造方法
*
* @param strKey
* 指定的密钥
* @throws Exception
*/
public DesUtils(String strKey) throws Exception {
//Security.addProvider(new com.sun.crypto.provider.SunJCE());
Key key = getKey(strKey.getBytes());
encryptCipher = Cipher.getInstance("DES");
encryptCipher.init(Cipher.ENCRYPT_MODE, key);
decryptCipher = Cipher.getInstance("DES");
decryptCipher.init(Cipher.DECRYPT_MODE, key);
}
/**
* 加密字节数组
*
* @param arrB
* 需加密的字节数组
* @return 加密后的字节数组
* @throws Exception
*/
public byte[] encrypt(byte[] arrB) throws Exception {
return encryptCipher.doFinal(arrB);
}
/**
* 加密字符串
*
* @param strIn
* 需加密的字符串
* @return 加密后的字符串
* @throws Exception
*/
public String encrypt(String strIn) throws Exception {
return byteArr2HexStr(encrypt(strIn.getBytes()));
}
/**
* 解密字节数组
*
* @param arrB
* 需解密的字节数组
* @return 解密后的字节数组
* @throws Exception
*/
public byte[] decrypt(byte[] arrB) throws Exception {
return decryptCipher.doFinal(arrB);
}
/**
* 解密字符串
*
* @param strIn
* 需解密的字符串
* @return 解密后的字符串
* @throws Exception
*/
public String decrypt(String strIn) throws Exception {
return new String(decrypt(hexStr2ByteArr(strIn)));
}
/**
* 从指定字符串生成密钥,密钥所需的字节数组长度为8位 不足8位时后面补0,超出8位只取前8位
*
* @param arrBTmp
* 构成该字符串的字节数组
* @return 生成的密钥
* @throws java.lang.Exception
*/
private Key getKey(byte[] arrBTmp) throws Exception {
// 创建一个空的8位字节数组(默认值为0)
byte[] arrB = new byte[8];
// 将原始字节数组转换为8位
for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
arrB[i] = arrBTmp[i];
}
// 生成密钥
Key key = new javax.crypto.spec.SecretKeySpec(arrB, "DES");
return key;
}
}

热评文章