В следующем посте будет показано, как написать простой аудит с использованием Spring AOP и аннотаций. Механизм аудита будет чистым, эффективным и простым в обслуживании (и Kewwl!).
Я продемонстрирую свой пример на системе управления пользователями (я полагаю, у вас есть общие знания по рефлексии и АОП).
Мы начнем с простой таблицы БД для хранения наших данных аудита:
1
2
3
4
5
6
7
|
`id`, `username` `user_type` `action` `target_user` `date` `user_ip` |
Нам нужно заполнить 4 основных поля (Username, UserType, Action, TargetUser)
* Имя пользователя — пользователь, который выполняет действие
* TargetUser — Target user действие выполняется.
Теперь давайте создадим новую аннотацию, чтобы отметить наш метод wanna-be-аудит. Мы собираемся быть очень «креативными» и использовать: @AuditAble
1
2
3
4
5
|
@Retention (RetentionPolicy.RUNTIME) @Target ({ElementType.METHOD,ElementType.TYPE}) public @interface Auditable { AuditingActionType actionType(); } |
Пример аннотированного метода @AuditAble:
1
2
3
4
5
6
7
|
@Override @Transactional @Auditable (actionType = AuditingActionType.INTERNAL_USER_REGISTRATION) public void createInternalUser(UserDTO userDTO) { userCreationService.createInternalUserOnDB(userDTO); } |
Наш будущий аспект (aop) будет собирать некоторые данные аудита из параметров метода с использованием DTO. В нашем случае целевое имя пользователя и actionType будут собраны в качестве нашей аудиторской информации.
Для этого я создал еще одну аннотацию AuditingTargetUsername :
1
2
3
4
5
|
@Retention (RetentionPolicy.RUNTIME) @Target ({ElementType.FIELD, ElementType.TYPE}) public @interface AuditingTargetUsername { String value() default "" ; } |
Итак, внутри UserDTO мы получили:
1
2
3
4
5
6
7
8
9
|
public abstract class UserDTO implements Serializable { @NotNull @AuditingTargetUsername private String userName; ... } |
Мы аннотировали userName с помощью @AuditingTargetUsername. Эта информация будет собрана позже.
Теперь давайте создадим аспект нашего АОП. Здесь вся логика аудита собрана и выполнена (перехват методов @Auditable, извлечение информации из аннотаций, использование репозитория для сохранения окончательной записи аудита):
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@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()))); } |
плохо объясняет основные области кода:
Pointcut — все @ Auditable.annotations
Совет — введите @After (мы хотим провести аудит после вызова метода)
Значение ActionType извлекается через объявление аннотированного метода:
@Auditable ( actionType = AuditingActionType.INTERNAL_USER_REGISTRATION )
auditingUsername — текущий пользователь, который выполняет действие (в нашем случае зарегистрированный пользователь). Я получил это через SecurityContext (Spring Security).
Теперь мы извлечем поле @targetAuditingUser через отражение во время выполнения:
01
02
03
04
05
06
07
08
09
10
|
targetAuditingUser = extractTargetAuditingUser(jp.getArgs()); ... public String extractTargetAuditingUserFromAnnotation(Object obj) { ... result = getTargetAuditingUserViaAnnotation(obj); ... } |
Вот логика для извлечения аннотированных полей с помощью отражения:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
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; } |
Результат на БД:
Вот и все. У нас есть чистая инфраструктура аудита, все, что вам нужно, это аннотировать ваш метод с помощью @Auditable и комментировать внутри ваших DTO / Entities необходимую информацию для аудита.
Идан.
Ссылка: | Аудит инфраструктуры для вашего приложения с использованием Spring AOP, пользовательских аннотаций и отражений от нашего партнера по JCG Идана Фридмана в блоге IdanFridman.com . |