Java对日志文件进行加密

最近碰到了一个新的需求,生产环境中Java程序部署的服务器会定期清理数据,需要将保存在程序所在服务器上的日志文件挂载到网盘上,但又不想让用户看到日志文件中的信息,因此需要对日志文件中的内容进行加密。
这里,并不是对日志文件中的敏感信息进行加密,而是对所有数据都进行加密。上网查了一圈资料之后,最终到了解决方案:自定义Appender,使用AES进行加密。下面贴出具体代码。
AES加密解密工具类

package com.lg.coding.util;import java.io.*;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;public class AESUtil {private static final String ALGORITHM = "AES";private static int offset = 16;private static final String transformation = "AES/CBC/PKCS5Padding";/*** AES加密字符串* @param password  密钥* @param value 待加密字符串*/public static String encrypt(String password, String value) {try {Key key = generateKey(password);//创建初始向量iv用于指定密钥偏移量IvParameterSpec iv = new IvParameterSpec(password.getBytes(), 0, offset);Cipher cipher = Cipher.getInstance(transformation);cipher.init(Cipher.ENCRYPT_MODE, key, iv);byte[] encryptedByteValue = cipher.doFinal(value.getBytes("utf-8"));String encryptedValue64 = Base64.getEncoder().encodeToString(encryptedByteValue);return encryptedValue64;} catch (Exception e) {e.printStackTrace();}return null;}/*** AES解密字符串* @param password  密钥* @param value 待解密字符串* @return*/public static String decrypt(String password, String value) {try {Key key = generateKey(password);//创建初始向量iv用于指定密钥偏移量IvParameterSpec iv = new IvParameterSpec(password.getBytes(), 0, offset);Cipher cipher = Cipher.getInstance(transformation);cipher.init(Cipher.DECRYPT_MODE, key, iv);byte[] decryptedValue64 = Base64.getDecoder().decode(value);byte[] decryptedByteValue = cipher.doFinal(decryptedValue64);String decryptedValue = new String(decryptedByteValue,"utf-8");return decryptedValue;} catch (Exception e) {e.printStackTrace();}return null;}/*** AES解密文件* @param password 密钥* @param inputFilePath 待解密文件路径* @param outputFilePath 输出文件路径*/public static void decryptFile(String password, String inputFilePath, String outputFilePath) {InputStream inputStream = null;BufferedReader bufferedReader = null;BufferedWriter bufferedWriter = null;try {inputStream = new FileInputStream(inputFilePath);bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));bufferedWriter = new BufferedWriter(new FileWriter(outputFilePath));String s;while ((s = bufferedReader.readLine()) != null) {bufferedWriter.write(decrypt(password, s));bufferedWriter.newLine();bufferedWriter.flush();}} catch (FileNotFoundException e) {System.out.println("找不到指定文件!");e.printStackTrace();} catch (IOException e) {System.out.println("文件读取错误!");e.printStackTrace();} finally {try {inputStream.close();bufferedReader.close();bufferedWriter.close();} catch (IOException e) {e.printStackTrace();}}}/*** 生成key* @param password* @return* @throws Exception*/private static Key generateKey(String password) {Key key = new SecretKeySpec(password.getBytes(),ALGORITHM);return key;}
}

在这里,加密操作和解密操作都是针对字符串进行的,在自定义Appender类中,重写subAppend方法,在执行输出文件操作之前对内容进行字符串加密;解密时,逐行读取文件内容后再进行字符串解密。
自定义Appender类

package com.lg.coding.util;import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.Layout;
import ch.qos.logback.core.rolling.RollingPolicy;
import ch.qos.logback.core.rolling.RollingPolicyBase;
import ch.qos.logback.core.rolling.RolloverFailure;
import ch.qos.logback.core.rolling.TriggeringPolicy;
import ch.qos.logback.core.rolling.helper.CompressionMode;
import ch.qos.logback.core.rolling.helper.FileNamePattern;
import ch.qos.logback.core.spi.DeferredProcessingAware;
import ch.qos.logback.core.status.ErrorStatus;
import ch.qos.logback.core.util.ContextUtil;
import org.slf4j.event.LoggingEvent;import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;public class CustomRollingFileAppender<E> extends FileAppender<E> {File currentlyActiveFile;TriggeringPolicy<E> triggeringPolicy;RollingPolicy rollingPolicy;private static String RFA_NO_TP_URL = "http://logback.qos.ch/codes.html#rfa_no_tp";private static String RFA_NO_RP_URL = "http://logback.qos.ch/codes.html#rfa_no_rp";private static String COLLISION_URL = "http://logback.qos.ch/codes.html#rfa_collision";private static String RFA_LATE_FILE_URL = "http://logback.qos.ch/codes.html#rfa_file_after";public CustomRollingFileAppender() {}public void start() {if (this.triggeringPolicy == null) {this.addWarn("No TriggeringPolicy was set for the RollingFileAppender named " + this.getName());this.addWarn("For more information, please visit " + RFA_NO_TP_URL);} else if (!this.triggeringPolicy.isStarted()) {this.addWarn("TriggeringPolicy has not started. RollingFileAppender will not start");} /*else if (this.checkForCollisionsInPreviousRollingFileAppenders()) {this.addError("Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting.");this.addError("For more information, please visit " + COLLISION_WITH_EARLIER_APPENDER_URL);}*/ else {if (!this.append) {this.addWarn("Append mode is mandatory for RollingFileAppender. Defaulting to append=true.");this.append = true;}if (this.rollingPolicy == null) {this.addError("No RollingPolicy was set for the RollingFileAppender named " + this.getName());this.addError("For more information, please visit " + RFA_NO_RP_URL);} /*else if (this.checkForFileAndPatternCollisions()) {this.addError("File property collides with fileNamePattern. Aborting.");this.addError("For more information, please visit " + COLLISION_URL);}*/ else {if (this.isPrudent()) {if (this.rawFileProperty() != null) {this.addWarn("Setting \"File\" property to null on account of prudent mode");this.setFile((String)null);}if (this.rollingPolicy.getCompressionMode() != CompressionMode.NONE) {this.addError("Compression is not supported in prudent mode. Aborting");return;}}this.currentlyActiveFile = new File(this.getFile());this.addInfo("Active log file name: " + this.getFile());super.start();}}}/*private boolean checkForFileAndPatternCollisions() {if (this.triggeringPolicy instanceof RollingPolicyBase) {RollingPolicyBase base = (RollingPolicyBase)this.triggeringPolicy;FileNamePattern fileNamePattern = base.fileNamePattern;if (fileNamePattern != null && this.fileName != null) {String regex = fileNamePattern.toRegex();return this.fileName.matches(regex);}}return false;}private boolean checkForCollisionsInPreviousRollingFileAppenders() {boolean collisionResult = false;if (this.triggeringPolicy instanceof RollingPolicyBase) {RollingPolicyBase base = (RollingPolicyBase)this.triggeringPolicy;FileNamePattern fileNamePattern = base.fileNamePattern;boolean collisionsDetected = this.innerCheckForFileNamePatternCollisionInPreviousRFA(fileNamePattern);if (collisionsDetected) {collisionResult = true;}}return collisionResult;}*/private boolean innerCheckForFileNamePatternCollisionInPreviousRFA(FileNamePattern fileNamePattern) {boolean collisionsDetected = false;Map<String, FileNamePattern> map = (Map)this.context.getObject("RFA_FILENAME_PATTERN_COLLISION_MAP");if (map == null) {return collisionsDetected;} else {Iterator var4 = map.entrySet().iterator();while(var4.hasNext()) {Map.Entry<String, FileNamePattern> entry = (Map.Entry)var4.next();if (fileNamePattern.equals(entry.getValue())) {this.addErrorForCollision("FileNamePattern", ((FileNamePattern)entry.getValue()).toString(), (String)entry.getKey());collisionsDetected = true;}}if (this.name != null) {map.put(this.getName(), fileNamePattern);}return collisionsDetected;}}public void stop() {super.stop();if (this.rollingPolicy != null) {this.rollingPolicy.stop();}if (this.triggeringPolicy != null) {this.triggeringPolicy.stop();}Map<String, FileNamePattern> map = ContextUtil.getFilenamePatternCollisionMap(this.context);if (map != null && this.getName() != null) {map.remove(this.getName());}}public void setFile(String file) {if (file != null && (this.triggeringPolicy != null || this.rollingPolicy != null)) {this.addError("File property must be set before any triggeringPolicy or rollingPolicy properties");this.addError("For more information, please visit " + RFA_LATE_FILE_URL);}super.setFile(file);}public String getFile() {return this.rollingPolicy.getActiveFileName();}public void rollover() {this.lock.lock();try {this.closeOutputStream();this.attemptRollover();this.attemptOpenFile();} finally {this.lock.unlock();}}private void attemptOpenFile() {try {this.currentlyActiveFile = new File(this.rollingPolicy.getActiveFileName());this.openFile(this.rollingPolicy.getActiveFileName());} catch (IOException var2) {this.addError("setFile(" + this.fileName + ", false) call failed.", var2);}}private void attemptRollover() {try {this.rollingPolicy.rollover();} catch (RolloverFailure var2) {this.addWarn("RolloverFailure occurred. Deferring roll-over.");this.append = true;}}protected void subAppend(E event) {if (this.isStarted()) {try {if (event instanceof DeferredProcessingAware) {((DeferredProcessingAware)event).prepareForDeferredProcessing();}byte[] byteArray = this.encoder.encode(event);//加密前数据String originalString = new String(byteArray, "UTF-8");//加密后数据String encryptedString = AESUtil.encrypt("Sanyuan123456789", originalString);this.writeBytes((encryptedString + "\n").getBytes());} catch (IOException var3) {this.started = false;this.addStatus(new ErrorStatus("IO failure in appender", this, var3));}}}private void writeBytes(byte[] byteArray) throws IOException {if (byteArray != null && byteArray.length != 0) {this.lock.lock();try {this.getOutputStream().write(byteArray);if (this.isImmediateFlush()) {this.getOutputStream().flush();}} finally {this.lock.unlock();}}}public RollingPolicy getRollingPolicy() {return this.rollingPolicy;}public TriggeringPolicy<E> getTriggeringPolicy() {return this.triggeringPolicy;}public void setRollingPolicy(RollingPolicy policy) {this.rollingPolicy = policy;if (this.rollingPolicy instanceof TriggeringPolicy) {this.triggeringPolicy = (TriggeringPolicy)policy;}}public void setTriggeringPolicy(TriggeringPolicy<E> policy) {this.triggeringPolicy = policy;if (policy instanceof RollingPolicy) {this.rollingPolicy = (RollingPolicy)policy;}}
}

这里是直接复制RollingFileAppender类的代码,对subAppend方法进行重写,在调用writeBytes()方法之前进行加密操作,将加密后的数据输出到本地。
本系统用的日志框架为SpringBoot内置的日志处理框架Logback。将logback-spring.xml文件中系统日志输出对应的Appender标签,class属性改为自定义Appender类的全路径:

<?xml version="1.0" encoding="UTF-8"?>
<configuration><include resource="org/springframework/boot/logging/logback/defaults.xml"/><springProperty scope="context" name="logDir" source="logging.path"/><!-- 日志存放路径 --><property name="log.path" value="${logDir}" /><!-- 日志输出格式 -->
<!--    <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />--><property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /><!-- 控制台输出 --><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><encoder><charset>utf8</charset><pattern>${log.pattern}</pattern></encoder><layout class="ch.qos.logback.classic.PatternLayout"><pattern>${log.pattern}</pattern></layout></appender><!-- 系统日志输出 --><appender name="file_info" class="com.lg.coding.util.CustomRollingFileAppender"><file>${log.path}/coding-info.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>${log.path}/coding-info.%d{yyyy-MM-dd}.%i.log</fileNamePattern><!-- 日志最大的历史 60--><maxHistory>60</maxHistory><maxFileSize>10MB</maxFileSize></rollingPolicy><encoder><pattern>${log.pattern}</pattern><charset>utf8</charset></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 过滤的级别 只会打印debug不会有info日志--><!--            <level>DEBUG</level>--><!-- 匹配时的操作:接收(记录) --><onMatch>ACCEPT</onMatch><!-- 不匹配时的操作:拒绝(不记录) --><onMismatch>DENY</onMismatch><level>INFO</level></filter></appender><appender name="file_error" class="com.lg.coding.util.CustomRollingFileAppender"><file>${log.path}/coding-error.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>coding/.%d{yyyy-MM-dd-HH:mm:ss}.gz</fileNamePattern><fileNamePattern>${log.path}/coding-error.%d{yyyy-MM-dd-HH}.log</fileNamePattern><!-- 日志最大的历史 60--><maxHistory>60</maxHistory></rollingPolicy><encoder><pattern>${log.pattern}</pattern><charset>utf8</charset></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 过滤的级别 --><level>ERROR</level><!-- 匹配时的操作:接收(记录) --><onMatch>ACCEPT</onMatch><!-- 不匹配时的操作:拒绝(不记录) --><onMismatch>DENY</onMismatch></filter></appender><appender name="file_debug" class="com.lg.coding.util.CustomRollingFileAppender"><file>${log.path}/coding-debug.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>${log.path}/coding-debug.%d{yyyy-MM-dd}.%i.log</fileNamePattern><!-- 日志最大的历史 60--><maxHistory>60</maxHistory><maxFileSize>10MB</maxFileSize></rollingPolicy><encoder><pattern>${log.pattern}</pattern><charset>utf8</charset></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 过滤的级别 只会打印debug不会有info日志--><!--            <level>DEBUG</level>--><!-- 匹配时的操作:接收(记录) --><onMatch>ACCEPT</onMatch><!-- 不匹配时的操作:拒绝(不记录) --><onMismatch>DENY</onMismatch><level>DEBUG</level></filter></appender><!-- 用户访问日志输出  --><appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/sys-user.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 按天回滚 daily --><fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern><!-- 日志最大的历史 60--><maxHistory>60</maxHistory></rollingPolicy><encoder><pattern>${log.pattern}</pattern></encoder></appender><!-- 系统模块日志级别控制  --><logger name="com.example" level="debug" /><!-- Spring日志级别控制  --><logger name="org.springframework" level="warn" /><root level="info"><appender-ref ref="console" /></root><!--系统操作日志--><root level="info"><appender-ref ref="console" /><appender-ref ref="file_info" /><appender-ref ref="file_error" /><appender-ref ref="file_debug" /></root><!--系统用户操作日志--><logger name="sys-user" level="info"><appender-ref ref="sys-user"/></logger>
</configuration>

此时,启动项目,查看系统本地日志文件:
在这里插入图片描述
可以看到,日志文件的内容已成功加密。
再写一个文件解密接口:

	@ApiOperation(value = "aes文件解密测试")@PostMapping("/aesFileDecrypteTest")public void aesFileDecrypteTest(String fileInputPath, String fileOutputPath) {String key = "Sanyuan123456789";AESUtil.decryptFile(key, fileInputPath, fileOutputPath);}

其中,fileInputPath和fileOutputPath两个参数分别为待解密文件所在路径和解密后的文件所在路径,接口调用中输入对应参数:
在这里插入图片描述
调用接口,可以看到,指定路径下生成了一个名为“解密日志.log”的文件, 打开文件,查看内容:
在这里插入图片描述
可以看到,内容已被成功解密。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/28476.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023牛客暑期多校训练营1(D/H/J/K)

目录 D.Chocolate H.Matches J.Roulette K.Subdivision D.Chocolate 思路&#xff1a;当n1且m1时候先手必输&#xff0c;然后1*k&#xff08;k>2&#xff09;的情况下后手必输&#xff0c;因为先手可以选到只剩下一个格子。而在其它情况里先手第一步可以先选(1,1)的格子…

ARM 架构是什么?

ARM&#xff08;Advanced RISC Machines&#xff09;架构是一种处理器架构&#xff0c;它是一种精简指令集计算机&#xff08;RISC&#xff09;架构。ARM架构最初由ARM Holdings&#xff08;现在是SoftBank Group的子公司&#xff09;开发&#xff0c;并在1980年代末和1990年代…

sort部分

sort主要针对文件内容的操作&#xff0c;对文件内容进行匹配或者过滤&#xff0c;排序 grep 过滤 针对文本内容进行过滤&#xff0c;也就是查找 -i&#xff1a;忽略大小写默认的&#xff0c;可以不加 -n&#xff1a;显示匹配的行号 -c&#xff1a;只统计匹配的行数 &#…

Python 列表 sort()函数使用详解

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;小白零基础《Python入门到精通》 sort函数使用详解 1、升序降序2、sort()和sorted()的区别3、切片排序4、指定排序…

地面点云提取:Autoware预处理ray_ground_filter节点解析 + 解决ray_ground_filter无输出的问题

文章目录 一、解决Autoware的ray_ground_filter节点无点云输出的问题二、ray_ground_filter节点代码分析2.1.监听bask_link和velodyne之间的TF2.2 裁切过高点云2.3 消除雷达近身反射点的影响2.4 角度和距离微分&#xff08;转换到柱坐标&#xff09;2.5 地面判断(核心部分) 一、…

Python numpy求均值、保留几位小数

import numpy as nplist_test [0.21, 0.32]print(f{np.mean(list_test):.2f}) #保留两位小数 print(f{np.mean(list_test):.3f}) #保留三位小数

网络虚拟化相关的Linux接口介绍

Linux拥有丰富的网络虚拟化功能&#xff0c;能被虚拟机&#xff0c;容器还有云网络使用。在这篇文章中&#xff0c;我会给出所有通用网络虚拟化接口的简要介绍。没有代码分析&#xff0c;只有简短的接口介绍和在Linux上的使用操作。这系列接口都可以使用ip link命令实现。 这篇…

Linux进程控制(一)---进程创建和终止(写时拷贝,exit与_exit等)

目录 进程创建 fork()函数 子进程如何继承父进程的数据 1.创建时拷贝分离 2.写时拷贝★ 进程终止 进程终止时&#xff0c;操作系统做了什么&#xff1f; 进程终止的常见方式 代码运行完毕&#xff0c;结果正确 退出码★ 代码运行完毕&#xff0c;结果不正确 代码异…

Echarts柱状图横向滚动,如何实现从后往前滚动

Echarts柱状图横向滚动&#xff0c;如何实现从后往前滚动 设置开始和结束的横坐标&#xff0c;设置产生横向滚动条

MVX-Net Multimodal VoxelNet for 3D Object Detection 论文学习

论文链接&#xff1a;MVX-Net Multimodal VoxelNet for 3D Object Detection 1. 解决了什么问题&#xff1f; 2D 目标检测取得了显著成效&#xff0c;但由于输入模态的本质区别&#xff0c;CNN 无法直接应用在 3D 检测任务。LiDAR 能准确地定位到 3D 空间的物体&#xff0c;基…

Spring Batch之读数据库——JdbcCursorItemReader之自定义PreparedStatementSetter(三十八)

一、自定义PreparedStatementSetter 详情参考我的另一篇博客&#xff1a; Spring Batch之读数据库——JdbcCursorItemReader&#xff08;三十五&#xff09;_人……杰的博客-CSDN博客 二、项目实例 1.项目实例 2.代码实现 BatchMain.java&#xff1a; package com.xj.dem…

electron+vue3全家桶+vite项目搭建【23】url唤醒应用,并传递参数

文章目录 引入实现效果实现步骤测试代码 引入 demo项目地址 很多场景下我们都希望通过url快速唤醒应用&#xff0c;例如百度网盘&#xff0c;在网页中唤醒应用&#xff0c;并传递下载链接&#xff0c;在electron中要实现这样的效果&#xff0c;就需要针对不同的平台做对应的处…