我有一个方面可以审计实体的创建、删除等。在审计中我必须获取触发实体更改的用户。
package com.sdx.rootservice.auditing.aspect;
import com.sdx.rootservice.auditing.model.AuditType;
import com.sdx.rootservice.auditing.service.interfaces.AuditLogService;
import com.sdx.rootservice.dto.lead.AppealTypeDto;
import com.sdx.rootservice.security.model.JwtAuthentication;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
@Aspect
@Log4j2
@RequiredArgsConstructor
public class AuditAspect {
private final AuditLogService auditLogService;
@AfterReturning(pointcut = "execution(* com.sdx.rootservice.controller.company.AppealTypeController.create(..))",
returning = "appealTypeDto")
public void afterAppealTypeCreated(AppealTypeDto appealTypeDto) {
var userId = fetchUserId();
var targetId = appealTypeDto.getId();
log.debug("Created new appeal type: " + appealTypeDto);
auditLogService.logChanges(userId, "AppealType", targetId, AuditType.CREATE);
}
@AfterReturning(pointcut = "execution(* com.sdx.rootservice.controller.company.AppealTypeController.delete(..)) && args(id)")
public void afterAppealTypeDeleted(Long id) {
var userId = fetchUserId();
log.debug("Deleted new appeal type: " + id);
auditLogService.logChanges(userId, "AppealType", id, AuditType.DELETE);
}
private Long fetchUserId() {
var authentication = (JwtAuthentication) SecurityContextHolder.getContext().getAuthentication();
return Optional.ofNullable(authentication)
.map(JwtAuthentication::getId)
.orElse(null);
}
}
恐怕,如果有多个创建端点的请求,安全性的上下文可能会发生变化,因为将运行不同的线程。在Spring AOP方面从Sring Security获取用户身份验证的正确解决方案是什么?
只要您不通过将系统属性
SecurityContextHolderStrategy
更改为 GlobalSecurityContextHolderStrategy
来将默认的 spring.security.strategy
更改为 MODE_GLOBAL
,并且可以确保 AuditAspect
将由受保护的 URL 调用spring security,你不需要担心它,因为 spring security 将确保当它完成处理 HTTP 请求时,它总是会清除 SecurityContext
。
在代码中,这是由
FilterChainProxy#doFilter()
的finally块在here完成的。