下一篇文章将演示如何使用Spring AOP和注释编写简单的审计。 审核机制将是干净,高效且易于维护的(和Kewwl!)。
我将在用户管理系统上演示我的示例(假设您对反射和AOP具有一般知识)。
我们从简单的数据库表开始,以保存我们的审计数据:
`id`,
`username`
`user_type`
`action`
`target_user`
`date`
`user_ip`
我们需要填充4个主要字段(用户名,用户类型,操作,目标用户)
*用户名–执行操作的用户
* TargetUser –执行操作的目标用户。
现在,让我们创建新的注释以标记我们的“想被审计”方法。 我们将非常“有创意”并使用:@AuditAble
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface Auditable {
AuditingActionType actionType();
}
带注释的@AuditAble方法示例:
@Override
@Transactional
@Auditable(actionType = AuditingActionType.INTERNAL_USER_REGISTRATION)
public void createInternalUser(UserDTO userDTO) {
userCreationService.createInternalUserOnDB(userDTO);
}
我们未来的Aspect(aop)将使用DTO从方法参数中收集一些审核数据。 在我们的情况下,将收集目标用户名和actionType作为我们的审核信息。
为此,我创建了另一个注释AuditingTargetUsername :
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface AuditingTargetUsername {
String value() default "";
}
因此,在UserDTO内部,我们得到了:
public abstract class UserDTO implements Serializable {
@NotNull
@AuditingTargetUsername
private String userName;
...
}
我们用@AuditingTargetUsername注释了userName。 该信息将在以后收集。
现在,让我们创建AOP的方面。 此处收集并执行所有审核逻辑(拦截@Auditable方法,从批注中提取信息,使用存储库保存最终审核记录):
@Aspect
public class AuditingAspect {
....
@After("@annotation(auditable)")
@Transactional
public void logAuditActivity(JoinPoint jp, Auditable auditable) {
String targetAuditingUser;
String actionType = auditable.actionType().getDescription();
String auditingUsername = Authentication auth = SecurityContextHolder.getContext().getAuthentication().getName()
role = userService.getCurrentUser(false).getPermissionsList().toString();
auditingUsernameIp = request.getRemoteAddr();
}
logger.info(
"Auditing information. auditingUsername=" + auditingUsername + ", actionType=" + actionType + ", role=" + role + ", targetAuditingUser="
+ targetAuditingUser + " auditingUsernameIp=" + auditingUsernameIp
);
auditingRepository
.save(new AuditingEntity(auditingUsername, role, actionType, targetAuditingUser, auditingUsernameIp,
new Timestamp(new java.util.Date().getTime())));
}
ill解释了主要的代码区域:
切入点–所有@ Auditable.annotations
建议–键入@After(我们希望在调用该方法之后进行审核)
通过带注释的方法的声明检索ActionType值:
@Auditable( actionType = AuditingActionType.INTERNAL_USER_REGISTRATION )
auditingUsername是执行操作的当前用户(在本例中为登录用户)。 我是通过SecurityContext(Spring Security)检索到的。
现在,我们将通过运行时的反射来提取@targetAuditingUser字段:
targetAuditingUser = extractTargetAuditingUser(jp.getArgs());
...
public String extractTargetAuditingUserFromAnnotation(Object obj) {
...
result = getTargetAuditingUserViaAnnotation(obj);
...
}
这就是通过反射提取带注释的字段的逻辑:
private String getTargetAuditingUserViaAnnotation(Object obj) {
class cl=obj.getClass()
String result = null;
try {
for (Field f : cl.getDeclaredFields())
for (Annotation a : f.getAnnotations()) {
if (a.annotationType() == AuditingTargetUsername.class) {
f.setAccessible(true);
Field annotatedFieldName = cl.getDeclaredField(f.getName());
annotatedFieldName.setAccessible(true);
String annotatedFieldVal = (String) annotatedFieldName.get(obj);
logger.debug("Found auditing annotation. type=" + a.annotationType() + " value=" + annotatedFieldVal.toString());
result = annotatedFieldVal;
}
}
} catch (Exception e) {
logger.error("Error extracting auditing annotations from obj" + obj.getClass());
}
return result;
}
DB上的结果:
而已。 我们拥有干净的审核基础结构,您所需要做的就是用@Auditable注释您的方法,并在DTO /实体内部注释所需的待审核信息。
伊丹