/**
* AES 对称加密、解密测试
*/
public static void aesTest() {
String plainText = "TCBJ-SECRET-KEY-ENCODE-DECODE-TEST";
try {
// 生成秘钥
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
/**
* todo
* 根据业务保存秘钥,秘钥一旦变化旧的内容无法解密
*/
// 加密
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encrypted = cipher.doFinal(plainText.getBytes());
// 解密
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println("输入:" + plainText);
System.out.println("加密结果:" + new String(encrypted));
System.out.println(decrypted);
System.out.println("解密结果:" + new String(decrypted));
} catch (Exception e) {
System.out.println("AES 加解密异常");
}
}
此处的keystore文件是用命令生成的,密码在命令中就定义了
不想用命令生成也可以在代码中直接生成
具体执行路径:JDK安装目录的 /bin 下 打开cmd 窗口
jceks 记得写对
keytool -genkey -alias csdn -keypass 123456 -keyalg RSA -keysize 1024 -validity 3650 -keystore D:/keyStore/test.keystore -storepass 888999 -storetype jceks
存储普通数据
/**
* keystore 文件秘钥存储测试
*/
public static void keyStoreTest() {
FileInputStream inputStream = null ;
OutputStream outputStream = null;
try {
// 读取 keyStore 文件转换为 keyStore 密钥库对象
inputStream = new FileInputStream("D:\\keyStore\\test.keystore");
// 设置证书类型 jceks
KeyStore keyStore = KeyStore.getInstance("jceks");
// 设置密钥库密码——获取keystore信息所需的密码
String storepass = "888999";
keyStore.load(inputStream,storepass.toCharArray());
inputStream.close();
// 加载keystore,就可以读取keystore现有条目、或者写入新条目
// 别名——创建文件指定好的别名及密码
String alias = "csdn";
// 别名密码,指定别名条目的密码——私钥密码
String keypass = "123456";
KeyStore.ProtectionParameter parameter = new KeyStore.PasswordProtection(keypass.toCharArray());
KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias,parameter);
PrivateKey myPrivateKey = entry.getPrivateKey();
System.out.println("获取到的私钥是:" + myPrivateKey.toString());
// 设置秘钥信息别名 desPws 及 访问密码 decrp pws ,写入存储信息 desPwd
String desPwd = "我是存储在 keystore 中的 AES 加密解密用的秘钥4.0";
String password = "decryp pws";
SecretKey mySecretKey = new SecretKeySpec(desPwd.getBytes(),"JKS");
KeyStore.SecretKeyEntry skEntry = new KeyStore.SecretKeyEntry(mySecretKey);
keyStore.setEntry("desPws", skEntry, new KeyStore.PasswordProtection(password.toCharArray()));
// 将 keystore 存储到指定输出流,并用密码保护完整性
outputStream = new FileOutputStream("D:\\keyStore\\test.keystore");
keyStore.store(outputStream,storepass.toCharArray());
outputStream.close();
} catch (Exception e) {
// todo 存储 keyStore 文件失败
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
// todo 关闭文件流失败
}
}
}
读取普通数据
/**
* keystore 文件秘钥提取测试
*/
public static void keyStoreDecodeTest() {
String storepass = "888999";
try {
FileInputStream inputStream = null;
// 读取keystore文件转换为 keystore 密钥库对象
inputStream = new FileInputStream("D:\\keyStore\\test.keystore");
// 设置证书类型
KeyStore keyStore = KeyStore.getInstance("jceks");
// 使用密钥库密码访问
keyStore.load(inputStream,storepass.toCharArray());
inputStream.close();
// 根据别名,从 证书获取密码并解密
// keystore.getKey 返回与给定别名关联的秘钥,并用给定密码来恢复他
String password = "decryp pws";
Key key = keyStore.getKey("desPws",password.toCharArray());
// key.getEncode 返回基本编码格式的秘钥,如果秘钥不支持编码,返回null
// 注意这里存储的是字符串,所以要根据 encode 转化,如果存储的是 SecretKey 直接 key 的值就是了
System.out.println("从证书中获取的秘钥为:" + new String(key.getEncoded()));
} catch (Exception e) {
// todo 异常处理
}
}
这里跟存储、读取普通数据其实差不多,多了一个生成 keystore 文件步骤而已
读取的时候 key 直接就是对应存储的 secretKey了
/**
* AES 秘钥对象 SecretKey 存储 KeyStore 中
*/
public static void saveSecretKey2KeyStore() {
try {
// 生成秘钥
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
KeyStore keyStore = KeyStore.getInstance("jceks");
keyStore.load(null,null);
System.out.println("保存的秘钥:" + secretKey);
// 可以存字符串也可以存对象
KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry(secretKey);
// 设置条目名称及密码
String entryPwd = "abc";
keyStore.setEntry("test",secretKeyEntry,new KeyStore.PasswordProtection(entryPwd.toCharArray()));
FileOutputStream outputStream = new FileOutputStream("D:\\keyStore\\test2.keystore");
// 设置文件访问密码
String storepwd = "123456";
keyStore.store(outputStream,storepwd.toCharArray());
outputStream.close();
} catch (Exception e) {
// todo
}
}
/**
* 从 KeyStore 获取 AES 秘钥对象 SecretKey
*/
public static void getSecretKeyFromKeyStore() {
try {
String storepass = "123456";
FileInputStream inputStream = new FileInputStream("D:\\keyStore\\test2.keystore");
KeyStore keyStore1 = KeyStore.getInstance("jceks");
keyStore1.load(inputStream,storepass.toCharArray());
inputStream.close();
String password = "abc";
Key key = keyStore1.getKey("test",password.toCharArray());
SecretKey keyValue = (SecretKey) key;
System.out.println("获取到的秘钥:" + keyValue);
} catch (Exception e) {
// todo
}
}
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
spring.cache.type=ehcache
spring.cache.ehcache.config=classpath:ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false">
<!--缓存存放路径-->
<diskStore path="D:\\keyStore\\"/>
<defaultCache maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="60"
timeToLiveSeconds="60"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />
<!--缓存文件名,配置多个不同的缓存存放不同的数据-->
<cache name="shortlinkPropertiesCache"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="150"
timeToLiveSeconds="600"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="1"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
<!--
要实现缓存的实体必须要序列化
以下属性是必须的:
name: Cache的名称,必须是唯一的(ehcache会把这个cache放到HashMap里)。
iskStore : 指定数据存储位置,可指定磁盘中的文件夹位置
defaultCache : 默认的管理策略
maxElementsInMemory: 在内存中缓存的element的最大数目。
maxElementsOnDisk: 在磁盘上缓存的element的最大数目,默认值为0,表示不限制。
eternal: 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,
如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断。
overflowToDisk: 如果内存中数据超过内存限制,是否要缓存到磁盘上。
以下属性是可选的:
timeToIdleSeconds: 对象空闲时间,指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问。
timeToLiveSeconds: 对象存活时间,指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问。
diskPersistent: 是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false。
diskExpiryThreadIntervalSeconds: 对象检测线程运行时间间隔。标识对象状态的线程多长时间运行一次。
diskSpoolBufferSizeMB: DiskStore使用的磁盘大小,默认值30MB。每个cache使用各自的DiskStore。
memoryStoreEvictionPolicy: 如果内存中数据超过内存限制,向磁盘缓存时的策略。默认值LRU,可选FIFO、LFU。
clearOnFlush:内存数量最大时是否清除。
缓存的3 种清空策略 :
FIFO ,first in first out (先进先出).
LFU , Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存。
LRU ,Least Recently Used(最近最少使用). (ehcache 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
import com.***.CacheTestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: chen
* @Date: 2023/7/18 10:09
*/
@RestController
public class test {
private CacheTestService cacheTestService;
private static final String SHORTLINKTXQ = "ShortLinkTXQ";
@Autowired
public test(CacheTestService cacheTestService) {
this.cacheTestService = cacheTestService;
}
@PostMapping("/addCache")
public void addCache(){
String result = cacheTestService.addCache(SHORTLINKTXQ);
System.out.println("结果:" + result);
}
@PostMapping("/deleteCache")
public void deleteCache() {
cacheTestService.deleteCache(SHORTLINKTXQ);
}
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
/**
* @Author: chen
* @Project: member-parent
* @Date: 2023/7/18 10:38
*/
@Service
@Slf4j
public class CacheTestService {
@Cacheable(value = "shortlinkPropertiesCache", key = "#code")
public String addCache(String code) {
//使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素
// 如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中
System.out.println("访问方法返回的数据");
// 直接正常返回对象、对象集合都行,都可以直接存入缓存,不用可以转成JSON之类的String 数据
return "缓存内容";
}
@CacheEvict(value = "shortlinkPropertiesCache", key = "#code")
public void deleteCache(String code) {
System.out.println("删除成功");
}
@CachePut(value = "shortlinkPropertiesCache", key = "#code")
public void updateCache(String code) {
//@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果
// 而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中
}
}