Open Liberty使用指南及微服务开发示例(六)
续前篇
二十二、实现租户日志访问审计(Audit Log for Log Access)
目前,我们已经实现 日志访问权限控制,但无法追踪 谁查看了哪些日志。
现在,我们要实现:
✅ 记录管理员查看日志的行为(包括时间、筛选条件)
✅ 系统管理员(Super Admin)可查看所有访问记录
✅ 日志访问审计记录存入数据库,防止滥用访问
方案设计
💡 审计哪些操作?
1️⃣ 管理员访问日志列表(包括筛选条件)
2️⃣ 管理员下载日志(CSV / Excel)
3️⃣ 管理员查看归档日志(AWS S3 / Google Drive)
💡 记录的内容:
- 访问者用户名
- 访问的筛选条件(时间范围、事件类型、用户名)
- 访问的时间
- 访问的 API 端点(普通查询 / 下载 / 归档日志)
1、创建日志访问审计表
📌 定义 LogAccessAudit
实体
import jakarta.persistence.*;
import java.time.LocalDateTime;@Entity
public class LogAccessAudit {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;private String action; // QUERY, DOWNLOAD, ARCHIVE_ACCESSprivate String filters;private LocalDateTime accessTime;@ManyToOne@JoinColumn(name = "organization_id")private Organization organization;
}
📌 记录日志访问信息,包括查询条件 ✅
2、拦截日志访问并记录
📌 修改 AuditLogResource.java
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.ext.Provider;
import java.time.LocalDateTime;
import jakarta.persistence.PersistenceContext;@Provider
public class LogAccessAuditFilter implements ContainerRequestFilter {@PersistenceContextprivate EntityManager em;@Overridepublic void filter(ContainerRequestContext requestContext) {String username = requestContext.getSecurityContext().getUserPrincipal().getName();String uri = requestContext.getUriInfo().getPath();String filters = requestContext.getUriInfo().getRequestUri().getQuery();Account account = em.createQuery("SELECT a FROM Account a WHERE a.username = :username", Account.class).setParameter("username", username).getSingleResult();LogAccessAudit audit = new LogAccessAudit();audit.setUsername(username);audit.setAction(uri.contains("export") ? "DOWNLOAD" : uri.contains("archive") ? "ARCHIVE_ACCESS" : "QUERY");audit.setFilters(filters);audit.setAccessTime(LocalDateTime.now());audit.setOrganization(account.getOrganization());em.persist(audit);}
}
📌 每次管理员访问日志,都会记录访问信息 ✅
3、查询日志访问记录
📌 修改 LogAccessAuditResource.java
@Path("/admin/log-access")
@Produces(MediaType.APPLICATION_JSON)
public class LogAccessAuditResource {@PersistenceContextprivate EntityManager em;@Contextprivate SecurityContext securityContext;@GETpublic List<LogAccessAudit> getLogAccessHistory() {Account currentUser = getCurrentUser();boolean isSuperAdmin = currentUser.getRole() == Role.SUPER_ADMIN;String query = "SELECT l FROM LogAccessAudit l WHERE (:isSuperAdmin = true OR l.organization.id = :tenantId) ORDER BY l.accessTime DESC";TypedQuery<LogAccessAudit> q = em.createQuery(query, LogAccessAudit.class);q.setParameter("isSuperAdmin", isSuperAdmin);if (!isSuperAdmin) {q.setParameter("tenantId", currentUser.getOrganization().getId());}return q.getResultList();}private Account getCurrentUser() {String username = securityContext.getUserPrincipal().getName();return em.createQuery("SELECT a FROM Account a WHERE a.username = :username", Account.class).setParameter("username", username).getSingleResult();}
}
📌 系统管理员可查看所有日志访问记录,租户管理员只能查看自己租户的访问记录 ✅
4、前端 API
📌 修改 api.js
export const getLogAccessHistory = async () => {const token = localStorage.getItem("token");return axios.get(`http://localhost:8000/api/admin/log-access`, {headers: { Authorization: `Bearer ${token}` }});
};
📌 管理员可查询日志访问记录 ✅
5、修改日志访问 UI
📌 创建 LogAccessHistory.js
import { useEffect, useState } from "react";
import { getLogAccessHistory } from "./api";export default function LogAccessHistory() {const [logs, setLogs] = useState([]);useEffect(() => {getLogAccessHistory().then(response => setLogs(response.data));}, []);return (<div><h2>Log Access History</h2><ul>{logs.map(log => (<li key={log.id}><strong>{log.username}</strong> - {log.action} - {log.filters} ({log.accessTime})</li>))}</ul></div>);
}
📌 管理员可访问 http://localhost:5173/admin/log-access
查看访问历史 ✅
总结
✅ 记录管理员访问日志的行为(查询 / 下载 / 归档)
✅ 系统管理员可查看所有访问记录
✅ 租户管理员只能查看自己租户的访问历史
✅ 前端 UI 适配日志访问审计管理
二十三、实现日志访问审计的自动归档(Log Access Audit Archiving)
目前,我们已经记录了管理员对日志的访问行为,但缺乏定期归档旧的日志访问记录功能。
现在,我们要实现:
✅ 定期归档过时的日志访问记录
✅ 归档数据存入外部存储(例如 AWS S3)
✅ 删除数据库中已归档的记录
✅ 根据日志访问时间判断是否过期
方案设计
💡 归档规则:
1️⃣ 日志访问记录超过某个时间范围(例如 1 年)自动归档
2️⃣ 将旧记录转移到外部存储(例如 AWS S3)
3️⃣ 将已归档的记录从数据库中删除
4️⃣ 归档过程中应保留原始记录的结构和内容
💡 归档方式:
- 定期执行归档任务(使用调度器,如 Quartz 或定时任务)
- 归档数据存储在 AWS S3 中,并生成唯一的归档文件名(如日期标识)
- 删除数据库中已归档的记录
1、定期归档日志的任务调度
📌 创建 LogArchiver.java
(定时任务)
import jakarta.ejb.Schedule;
import jakarta.ejb.Singleton;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.time.LocalDateTime;
import java.util.List;@Singleton
public class LogArchiver {@PersistenceContextprivate EntityManager em;@Schedule(hour = "0", minute = "0", persistent = false) // 每天午夜执行归档任务public void archiveOldLogs() {LocalDateTime oneYearAgo = LocalDateTime.now().minusYears(1);List<LogAccessAudit> logsToArchive = em.createQuery("SELECT l FROM LogAccessAudit l WHERE l.accessTime < :oneYearAgo", LogAccessAudit.class).setParameter("oneYearAgo", oneYearAgo).getResultList();if (!logsToArchive.isEmpty()) {archiveToS3(logsToArchive);deleteArchivedLogs(logsToArchive);}}private void archiveToS3(List<LogAccessAudit> logs) {// 生成唯一归档文件名(如:log-access-audit-2024-02-09.json)String archiveFileName = "log-access-audit-" + LocalDateTime.now().toLocalDate() + ".json";// 将日志转换为 JSON 格式(可以使用 Jackson 或 Gson 序列化)String jsonContent = convertLogsToJson(logs);// 上传到 AWS S3(假设你已经配置了 AWS SDK)uploadToS3(archiveFileName, jsonContent);}private String convertLogsToJson(List<LogAccessAudit> logs) {// 使用 Jackson 或 Gson 进行 JSON 转换// 示例:new ObjectMapper().writeValueAsString(logs)return "JSON_STRING";}private void uploadToS3(String fileName, String content) {// 使用 AWS SDK 将文件上传到 S3// S3Client client = S3Client.builder().build();// PutObjectRequest putObjectRequest = PutObjectRequest.builder().bucket("your-bucket-name").key(fileName).build();// client.putObject(putObjectRequest, RequestBody.fromString(content));}private void deleteArchivedLogs(List<LogAccessAudit> logs) {for (LogAccessAudit log : logs) {em.remove(log);}}
}
📌 每晚 00:00 自动执行归档任务,将 1 年前的日志转移到 S3,并从数据库中删除已归档的记录 ✅
2、配置 AWS S3
首先,确保你的项目中已经集成了 AWS SDK,用于将归档文件上传到 S3。
📌 在 pom.xml
中添加 AWS SDK 依赖
<dependency><groupId>software.amazon.awssdk</groupId><artifactId>s3</artifactId><version>2.17.112</version>
</dependency>
📌 配置 AWS 客户端
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;public class S3Util {private static final S3Client s3Client = S3Client.builder().credentialsProvider(DefaultCredentialsProvider.create()).region(Region.US_EAST_1) // 选择你实际的区域.build();public static void uploadToS3(String bucketName, String key, String content) {PutObjectRequest putObjectRequest = PutObjectRequest.builder().bucket(bucketName).key(key).build();s3Client.putObject(putObjectRequest, RequestBody.fromString(content));}
}
📌 修改 LogArchiver
中的 uploadToS3
调用 AWS SDK:
private void uploadToS3(String fileName, String content) {S3Util.uploadToS3("your-bucket-name", fileName, content);
}
确保 AWS 账户和权限配置正确,以便应用能够向指定的 S3 存储桶上传文件。
3、配置数据库清理策略
归档操作完成后,从数据库删除已归档的记录:
private void deleteArchivedLogs(List<LogAccessAudit> logs) {for (LogAccessAudit log : logs) {em.remove(log);}em.flush(); // 确保删除操作生效
}
📌 这样能确保数据库中只保留最近一年的日志记录 ✅
4、日志归档管理 UI(可选)
你还可以为管理员提供一个 UI,查看已归档的日志和文件信息。
📌 创建 ArchivedLogList.js
import { useState, useEffect } from "react";
import { getArchivedLogs } from "./api";export default function ArchivedLogList() {const [logs, setLogs] = useState([]);useEffect(() => {getArchivedLogs().then(response => setLogs(response.data));}, []);return (<div><h2>Archived Logs</h2><ul>{logs.map(log => (<li key={log.id}><a href={`/archive/${log.fileName}`} target="_blank">{log.fileName} - {log.date}</a></li>))}</ul></div>);
}
📌 管理员可查看归档文件列表(可点击下载) ✅
总结
✅ 每晚 00:00 自动执行归档任务,将 1 年前的日志转移到 S3
✅ 数据库中删除已归档的日志记录,避免占用空间
✅ 管理员可查看归档日志文件并下载
✅ 日志归档文件存储在 AWS S3 中,确保安全和高可用性
二十四、实现日志归档的下载权限控制(Download Permission for Archived Logs)
目前,我们已实现日志归档,但缺乏对归档日志下载的权限控制。
现在,我们要实现:
✅ 根据管理员的角色来控制归档文件的下载权限
✅ 系统管理员可下载所有归档文件
✅ 租户管理员只能下载自己租户的归档文件
方案设计
💡 下载权限规则:
1️⃣ 系统管理员(Super Admin):可下载所有租户的日志归档文件
2️⃣ 租户管理员:只能下载自己租户的日志归档文件
3️⃣ 普通用户:没有下载权限,只有浏览权限
1、更新 LogAccessAudit
表,记录归档日志的归属
首先,我们需要确保每个归档文件与其所属的租户有对应关系。
📌 修改 LogAccessAudit
表
@Entity
public class LogAccessAudit {// ... 之前的字段@ManyToOne@JoinColumn(name = "organization_id")private Organization organization;@Column(name = "is_archived")private boolean isArchived;// ... getter & setter
}
📌 新增字段 isArchived
用来标记日志是否已经归档 ✅
2、修改归档过程,确保文件与租户关联
在归档过程中,除了将日志转移到外部存储,还要标记日志为已归档,并与对应的租户关联。
📌 修改 LogArchiver
private void archiveToS3(List<LogAccessAudit> logs) {// 生成唯一归档文件名(如:log-access-audit-2024-02-09.json)String archiveFileName = "log-access-audit-" + LocalDateTime.now().toLocalDate() + ".json";String tenantId = logs.get(0).getOrganization().getId().toString(); // 获取租户ID// 将日志转换为 JSON 格式(可以使用 Jackson 或 Gson 序列化)String jsonContent = convertLogsToJson(logs);// 上传到 AWS S3(假设你已经配置了 AWS SDK)uploadToS3(archiveFileName, jsonContent);// 标记日志为已归档for (LogAccessAudit log : logs) {log.setIsArchived(true);em.merge(log); // 更新日志状态}
}
📌 归档后的日志将标记为已归档,并记录其所属租户 ✅
3、控制下载权限
我们需要在下载请求中检查管理员的权限,只有符合条件的用户才能下载归档文件。
📌 修改 LogAccessAuditResource.java
@Path("/admin/log-access/archive/{fileName}")
@Produces(MediaType.APPLICATION_JSON)
public class LogAccessAuditResource {@PersistenceContextprivate EntityManager em;@Contextprivate SecurityContext securityContext;@GET@Path("/download")public Response downloadArchivedLog(@PathParam("fileName") String fileName) {Account currentUser = getCurrentUser();LogAccessAudit archivedLog = em.createQuery("SELECT l FROM LogAccessAudit l WHERE l.fileName = :fileName", LogAccessAudit.class).setParameter("fileName", fileName).getSingleResult();if (archivedLog == null) {return Response.status(Response.Status.NOT_FOUND).entity("File not found").build();}// 检查权限boolean isSuperAdmin = currentUser.getRole() == Role.SUPER_ADMIN;boolean isTenantAdmin = currentUser.getRole() == Role.TENANT_ADMIN && archivedLog.getOrganization().getId().equals(currentUser.getOrganization().getId());if (!isSuperAdmin && !isTenantAdmin) {return Response.status(Response.Status.FORBIDDEN).entity("Access denied").build();}// 从 S3 下载文件String fileContent = downloadFromS3(fileName); // 从 S3 获取文件内容return Response.ok(fileContent).build();}private String downloadFromS3(String fileName) {// 使用 AWS SDK 下载文件// S3Client client = S3Client.builder().build();// GetObjectRequest getObjectRequest = GetObjectRequest.builder().bucket("your-bucket-name").key(fileName).build();// return client.getObject(getObjectRequest, ResponseTransformer.toBytes()).asString();return "File content from S3";}private Account getCurrentUser() {String username = securityContext.getUserPrincipal().getName();return em.createQuery("SELECT a FROM Account a WHERE a.username = :username", Account.class).setParameter("username", username).getSingleResult();}
}
📌 系统管理员可下载所有文件,租户管理员只能下载自己租户的文件 ✅
4、修改前端 UI,适配权限控制
在前端,我们需要根据管理员角色来控制下载按钮的显示/隐藏。
📌 修改 LogAccessHistory.js
import { useEffect, useState } from "react";
import { getLogAccessHistory } from "./api";export default function LogAccessHistory() {const [logs, setLogs] = useState([]);const [role, setRole] = useState(""); // 当前用户角色useEffect(() => {// 获取当前用户角色(假设通过 API 获取)const userRole = localStorage.getItem("role");setRole(userRole);// 获取日志访问历史getLogAccessHistory().then(response => setLogs(response.data));}, []);return (<div><h2>Log Access History</h2><ul>{logs.map(log => (<li key={log.id}><strong>{log.username}</strong> - {log.action} - {log.filters} ({log.accessTime}){role === "SUPER_ADMIN" || log.organizationId === localStorage.getItem("organizationId") ? (<button onClick={() => downloadLog(log.fileName)}>Download</button>) : null}</li>))}</ul></div>);
}const downloadLog = (fileName) => {fetch(`/api/admin/log-access/archive/${fileName}/download`, {method: "GET",headers: {"Authorization": `Bearer ${localStorage.getItem("token")}`}}).then(response => response.text()).then(content => {// 处理文件内容console.log(content);}).catch(err => {console.error("Download failed", err);});
};
📌 根据用户角色(SUPER_ADMIN
或 TENANT_ADMIN
)来控制下载按钮 ✅
总结
✅ 根据管理员角色控制日志归档的下载权限
✅ 系统管理员可以下载所有租户的归档文件
✅ 租户管理员只能下载自己租户的归档文件
✅ 前端 UI 会根据权限动态显示下载按钮
二十五、 为日志归档文件增加审计功能(Log Download Auditing)
我们需要记录每次日志归档文件的下载操作,包括:
✅ 下载者(管理员)的身份信息
✅ 下载的归档文件名
✅ 下载时间和 IP 地址
✅ 下载操作的成功与否
方案设计
💡 审计记录规则:
1️⃣ 每次下载操作都会生成审计记录
2️⃣ 记录操作用户、文件名、时间、IP 地址以及操作结果
3️⃣ 可以在系统中查看历史下载审计日志
1、创建审计记录实体 LogDownloadAudit
我们需要定义一个新的实体类 LogDownloadAudit
,用于存储下载的审计信息。
📌 创建 LogDownloadAudit.java
@Entity
public class LogDownloadAudit {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@ManyToOne@JoinColumn(name = "account_id")private Account account;private String fileName;private LocalDateTime downloadTime;private String ipAddress;private boolean success;// getter & setter
}
📌 字段说明:
account
:操作用户的账户fileName
:下载的归档文件名downloadTime
:下载的时间戳ipAddress
:发起下载请求的 IP 地址success
:下载操作是否成功
2、在下载操作中记录审计信息
在 LogAccessAuditResource.java
中修改下载逻辑,记录每次下载的审计信息。
📌 修改 LogAccessAuditResource.java
@Path("/admin/log-access/archive/{fileName}")
@Produces(MediaType.APPLICATION_JSON)
public class LogAccessAuditResource {@PersistenceContextprivate EntityManager em;@Contextprivate SecurityContext securityContext;@GET@Path("/download")public Response downloadArchivedLog(@PathParam("fileName") String fileName) {Account currentUser = getCurrentUser();LogAccessAudit archivedLog = em.createQuery("SELECT l FROM LogAccessAudit l WHERE l.fileName = :fileName", LogAccessAudit.class).setParameter("fileName", fileName).getSingleResult();if (archivedLog == null) {recordAudit(currentUser, fileName, false);return Response.status(Response.Status.NOT_FOUND).entity("File not found").build();}// 检查权限boolean isSuperAdmin = currentUser.getRole() == Role.SUPER_ADMIN;boolean isTenantAdmin = currentUser.getRole() == Role.TENANT_ADMIN && archivedLog.getOrganization().getId().equals(currentUser.getOrganization().getId());if (!isSuperAdmin && !isTenantAdmin) {recordAudit(currentUser, fileName, false);return Response.status(Response.Status.FORBIDDEN).entity("Access denied").build();}// 从 S3 下载文件String fileContent = downloadFromS3(fileName); // 从 S3 获取文件内容// 记录下载成功的审计信息recordAudit(currentUser, fileName, true);return Response.ok(fileContent).build();}private String downloadFromS3(String fileName) {// 使用 AWS SDK 下载文件return "File content from S3";}private void recordAudit(Account currentUser, String fileName, boolean success) {LogDownloadAudit audit = new LogDownloadAudit();audit.setAccount(currentUser);audit.setFileName(fileName);audit.setDownloadTime(LocalDateTime.now());audit.setIpAddress(getClientIpAddress());audit.setSuccess(success);em.persist(audit); // 保存审计记录}private String getClientIpAddress() {// 获取客户端的 IP 地址return "127.0.0.1"; // 假设这是获取的 IP 地址,可以从请求头中获取}private Account getCurrentUser() {String username = securityContext.getUserPrincipal().getName();return em.createQuery("SELECT a FROM Account a WHERE a.username = :username", Account.class).setParameter("username", username).getSingleResult();}
}
📌 每次下载操作都会调用 recordAudit()
方法,保存用户、文件名、时间、IP 地址和操作结果到数据库。 ✅
3、查看审计日志
我们可以为管理员提供一个接口和页面来查看下载审计日志。
📌 创建审计日志查询接口
@Path("/admin/log-access/audit")
public class LogDownloadAuditResource {@PersistenceContextprivate EntityManager em;@GET@Produces(MediaType.APPLICATION_JSON)public List<LogDownloadAudit> getAllDownloadAudits() {return em.createQuery("SELECT l FROM LogDownloadAudit l", LogDownloadAudit.class).getResultList();}
}
📌 返回所有下载审计记录。可以根据需求添加分页和过滤功能。
4、管理员查看下载审计日志 UI
为管理员提供一个 UI 界面,显示所有下载审计记录。
📌 修改 AuditLogList.js
import { useEffect, useState } from "react";
import { getDownloadAudits } from "./api";export default function AuditLogList() {const [audits, setAudits] = useState([]);useEffect(() => {getDownloadAudits().then(response => setAudits(response.data));}, []);return (<div><h2>Download Audits</h2><table><thead><tr><th>User</th><th>File</th><th>Download Time</th><th>IP Address</th><th>Success</th></tr></thead><tbody>{audits.map(audit => (<tr key={audit.id}><td>{audit.account.username}</td><td>{audit.fileName}</td><td>{audit.downloadTime}</td><td>{audit.ipAddress}</td><td>{audit.success ? "Yes" : "No"}</td></tr>))}</tbody></table></div>);
}
📌 显示所有下载审计记录,包括用户、文件名、下载时间、IP 地址和操作结果 ✅
总结
✅ 每次下载归档文件都会生成审计记录
✅ 记录用户、文件名、下载时间、IP 地址和操作结果
✅ 管理员可通过 UI 查看所有下载审计日志
二十六、 增强日志审计功能,根据文件类型或操作类型进行分类审计
我们将增强现有的日志审计功能,通过分类审计来按文件类型(如日志、报告等)或操作类型(如下载、删除等)对下载操作进行记录。
方案设计
💡 分类审计规则:
1️⃣ 按文件类型分类审计:例如,将日志文件、报告文件等分开存储和记录。
2️⃣ 按操作类型分类审计:例如,记录下载、删除、修改等操作,方便区分不同的操作类型。
3️⃣ 审计数据存储:将分类信息存储在审计表中,方便查询和统计。
1、修改 LogDownloadAudit
表,增加分类字段
为了实现分类审计,我们需要在审计表中添加新的字段,分别记录文件类型和操作类型。
📌 修改 LogDownloadAudit.java
@Entity
public class LogDownloadAudit {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@ManyToOne@JoinColumn(name = "account_id")private Account account;private String fileName;private LocalDateTime downloadTime;private String ipAddress;private boolean success;// 新增字段:文件类型和操作类型private String fileType; // 记录文件类型(如:日志、报告等)private String operationType; // 记录操作类型(如:下载、删除等)// getter & setter
}
📌 字段说明:
fileType
:文件类型(例如:日志文件、报告文件等)operationType
:操作类型(例如:下载、删除等)
2、更新下载操作逻辑,记录分类信息
我们需要在下载操作中根据文件的实际类型和操作的实际类型来记录审计信息。
📌 修改 LogAccessAuditResource.java
@Path("/admin/log-access/archive/{fileName}")
@Produces(MediaType.APPLICATION_JSON)
public class LogAccessAuditResource {@PersistenceContextprivate EntityManager em;@Contextprivate SecurityContext securityContext;@GET@Path("/download")public Response downloadArchivedLog(@PathParam("fileName") String fileName) {Account currentUser = getCurrentUser();LogAccessAudit archivedLog = em.createQuery("SELECT l FROM LogAccessAudit l WHERE l.fileName = :fileName", LogAccessAudit.class).setParameter("fileName", fileName).getSingleResult();if (archivedLog == null) {recordAudit(currentUser, fileName, false, "LOG", "DOWNLOAD");return Response.status(Response.Status.NOT_FOUND).entity("File not found").build();}// 检查权限boolean isSuperAdmin = currentUser.getRole() == Role.SUPER_ADMIN;boolean isTenantAdmin = currentUser.getRole() == Role.TENANT_ADMIN && archivedLog.getOrganization().getId().equals(currentUser.getOrganization().getId());if (!isSuperAdmin && !isTenantAdmin) {recordAudit(currentUser, fileName, false, "LOG", "DOWNLOAD");return Response.status(Response.Status.FORBIDDEN).entity("Access denied").build();}// 从 S3 下载文件String fileContent = downloadFromS3(fileName); // 从 S3 获取文件内容// 记录下载成功的审计信息recordAudit(currentUser, fileName, true, "LOG", "DOWNLOAD");return Response.ok(fileContent).build();}private String downloadFromS3(String fileName) {return "File content from S3";}private void recordAudit(Account currentUser, String fileName, boolean success, String fileType, String operationType) {LogDownloadAudit audit = new LogDownloadAudit();audit.setAccount(currentUser);audit.setFileName(fileName);audit.setDownloadTime(LocalDateTime.now());audit.setIpAddress(getClientIpAddress());audit.setSuccess(success);audit.setFileType(fileType); // 设置文件类型audit.setOperationType(operationType); // 设置操作类型em.persist(audit); // 保存审计记录}private String getClientIpAddress() {return "127.0.0.1"; // 获取客户端的 IP 地址}private Account getCurrentUser() {String username = securityContext.getUserPrincipal().getName();return em.createQuery("SELECT a FROM Account a WHERE a.username = :username", Account.class).setParameter("username", username).getSingleResult();}
}
📌 新增功能:
- 文件类型 (
fileType
):根据文件类型设置(例如:日志、报告等) - 操作类型 (
operationType
):根据操作类型设置(例如:下载、删除等)
3、增加文件类型和操作类型的过滤查询
为了更方便地查看分类后的审计记录,我们可以在查询时支持按文件类型或操作类型进行过滤。
📌 修改 LogDownloadAuditResource.java
@Path("/admin/log-access/audit")
public class LogDownloadAuditResource {@PersistenceContextprivate EntityManager em;@GET@Produces(MediaType.APPLICATION_JSON)public List<LogDownloadAudit> getDownloadAudits(@QueryParam("fileType") String fileType, @QueryParam("operationType") String operationType) {StringBuilder queryStr = new StringBuilder("SELECT l FROM LogDownloadAudit l WHERE 1=1");if (fileType != null) {queryStr.append(" AND l.fileType = :fileType");}if (operationType != null) {queryStr.append(" AND l.operationType = :operationType");}TypedQuery<LogDownloadAudit> query = em.createQuery(queryStr.toString(), LogDownloadAudit.class);if (fileType != null) {query.setParameter("fileType", fileType);}if (operationType != null) {query.setParameter("operationType", operationType);}return query.getResultList();}
}
📌 新增功能:
- 按
fileType
(文件类型)进行过滤 - 按
operationType
(操作类型)进行过滤
4、管理员查看分类审计日志 UI
管理员可以通过 UI 来过滤审计记录,查看不同文件类型和操作类型的下载记录。
📌 修改 AuditLogList.js
import { useEffect, useState } from "react";
import { getDownloadAudits } from "./api";export default function AuditLogList() {const [audits, setAudits] = useState([]);const [fileType, setFileType] = useState("");const [operationType, setOperationType] = useState("");useEffect(() => {getDownloadAudits(fileType, operationType).then(response => setAudits(response.data));}, [fileType, operationType]);return (<div><h2>Download Audits</h2><div><label>File Type:</label><select onChange={(e) => setFileType(e.target.value)}><option value="">All</option><option value="LOG">Log Files</option><option value="REPORT">Report Files</option></select></div><div><label>Operation Type:</label><select onChange={(e) => setOperationType(e.target.value)}><option value="">All</option><option value="DOWNLOAD">Download</option><option value="DELETE">Delete</option></select></div><table><thead><tr><th>User</th><th>File</th><th>Download Time</th><th>IP Address</th><th>Success</th><th>File Type</th><th>Operation Type</th></tr></thead><tbody>{audits.map(audit => (<tr key={audit.id}><td>{audit.account.username}</td><td>{audit.fileName}</td><td>{audit.downloadTime}</td><td>{audit.ipAddress}</td><td>{audit.success ? "Yes" : "No"}</td><td>{audit.fileType}</td><td>{audit.operationType}</td></tr>))}</tbody></table></div>);
}
📌 管理员可以按文件类型和操作类型进行过滤和查看。
总结
✅ 为日志归档下载操作增加了分类审计功能:按文件类型和操作类型分类记录。
✅ 管理员可以过滤查看不同文件类型或操作类型的审计记录。
✅ 前端支持按条件(文件类型、操作类型)显示审计日志。
全文完