在控制器上 spring PreAuthorize 之后使用 AspectJ 进行审计
Audit using AspectJ after spring PreAuthorize on controller
我正在尝试审核 Spring 应用程序中成功和失败的 OAuth2 身份验证尝试的日志。我正在使用 Spring 的 @PreAuthorize 注释,如下所示。
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@PreAuthorize("#oauth2.hasScope('read')")
public Person getById(@PathVariable String id) {
return service.getById(id);
}
认证授权正常。为了尝试进行审计日志记录,我尝试使用 AspectJ 进行一些 AOP,如下所示。
@Component
@Aspect
public class AuditLogger {
@After("@annotation(org.springframework.security.access.prepost.PreAuthorize)")
public void afterAuth(JoinPoint joinPoint) throws Throwable {
// do audit logging
}
}
但是,只有在身份验证和授权都成功时才会发生这种情况。这些是场景:
- 给定具有读取范围的有效令牌 - afterAuth 被命中
- 给出的令牌无效 - afterAuth 未命中
- 没有读取范围的给定有效令牌 - afterAuth 未命中
我最好的猜测是 Spring 在身份验证或授权失败时会做一些不同的事情,但我不确定如何解决它。有什么建议吗?
在一个文件中找不到执行此操作的好方法,所以我最终使用一个文件来处理失败的身份验证请求(使用事件),另一个文件处理成功的身份验证请求(使用方面)。我用一个自定义的 MyAnnotation 注释了我的所有控制器,其中包含一些关于控制器的信息。这可能不是最好的解决方案,但希望它能帮助别人。
失败:
@Component
public class FailedAuthRequestAuditor {
private ApplicationContext context;
private AuditEventLogger logger;
@Autowired
public FailedAuthRequestAuditor(
ApplicationContext context,
AuditEventLogger logger
) {
this.context = context;
this.logger = logger;
}
@EventListener
public void onBadCredentialsEvent(AuthenticationFailureBadCredentialsEvent event) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Method destinationMethod = this.getDestination(request);
if (destinationMethod != null) {
// do logging here
}
}
@EventListener
public void onAuthorizationFailureEvent(AuthorizationFailureEvent event) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Method destinationMethod = this.getDestination(request);
if (destinationMethod != null) {
// do logging here
}
}
private Method getDestination(HttpServletRequest request) {
for (HandlerMapping handlerMapping : context.getBeansOfType(HandlerMapping.class).values()) {
HandlerExecutionChain handlerExecutionChain = null;
try {
handlerExecutionChain = handlerMapping.getHandler(request);
} catch (Exception e) {
// do nothing
}
if (handlerExecutionChain != null && handlerExecutionChain.getHandler() instanceof HandlerMethod) {
return ((HandlerMethod) handlerExecutionChain.getHandler()).getMethod();
}
}
return null;
}
}
成功:
@Aspect
@Component
public class SuccessfulAuthRequestAuditor {
private AuditEventLogger auditEventLogger;
private AuditEventOutcomeMapper mapper;
@Autowired
public SuccessfulAuthRequestAuditor(AuditEventLogger auditEventLogger, AuditEventOutcomeMapper mapper) {
this.auditEventLogger = auditEventLogger;
this.mapper = mapper;
}
@AfterReturning(pointcut = "execution(@com.company.MyAnnotation * *(..)) && @annotation(myAnnotation) && args(request,..)", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result, MyAnnotation myAnnotation, HttpServletRequest request) {
// do logging
}
@AfterThrowing(pointcut = "execution(@com.company.MyAnnotation * *(..)) && @annotation(myAnnotation) && args(request,..)", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Throwable exception, MyAnnotation myAnnotation, HttpServletRequest request) {
// do logging
}
}
我正在尝试审核 Spring 应用程序中成功和失败的 OAuth2 身份验证尝试的日志。我正在使用 Spring 的 @PreAuthorize 注释,如下所示。
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@PreAuthorize("#oauth2.hasScope('read')")
public Person getById(@PathVariable String id) {
return service.getById(id);
}
认证授权正常。为了尝试进行审计日志记录,我尝试使用 AspectJ 进行一些 AOP,如下所示。
@Component
@Aspect
public class AuditLogger {
@After("@annotation(org.springframework.security.access.prepost.PreAuthorize)")
public void afterAuth(JoinPoint joinPoint) throws Throwable {
// do audit logging
}
}
但是,只有在身份验证和授权都成功时才会发生这种情况。这些是场景:
- 给定具有读取范围的有效令牌 - afterAuth 被命中
- 给出的令牌无效 - afterAuth 未命中
- 没有读取范围的给定有效令牌 - afterAuth 未命中
我最好的猜测是 Spring 在身份验证或授权失败时会做一些不同的事情,但我不确定如何解决它。有什么建议吗?
在一个文件中找不到执行此操作的好方法,所以我最终使用一个文件来处理失败的身份验证请求(使用事件),另一个文件处理成功的身份验证请求(使用方面)。我用一个自定义的 MyAnnotation 注释了我的所有控制器,其中包含一些关于控制器的信息。这可能不是最好的解决方案,但希望它能帮助别人。
失败:
@Component
public class FailedAuthRequestAuditor {
private ApplicationContext context;
private AuditEventLogger logger;
@Autowired
public FailedAuthRequestAuditor(
ApplicationContext context,
AuditEventLogger logger
) {
this.context = context;
this.logger = logger;
}
@EventListener
public void onBadCredentialsEvent(AuthenticationFailureBadCredentialsEvent event) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Method destinationMethod = this.getDestination(request);
if (destinationMethod != null) {
// do logging here
}
}
@EventListener
public void onAuthorizationFailureEvent(AuthorizationFailureEvent event) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Method destinationMethod = this.getDestination(request);
if (destinationMethod != null) {
// do logging here
}
}
private Method getDestination(HttpServletRequest request) {
for (HandlerMapping handlerMapping : context.getBeansOfType(HandlerMapping.class).values()) {
HandlerExecutionChain handlerExecutionChain = null;
try {
handlerExecutionChain = handlerMapping.getHandler(request);
} catch (Exception e) {
// do nothing
}
if (handlerExecutionChain != null && handlerExecutionChain.getHandler() instanceof HandlerMethod) {
return ((HandlerMethod) handlerExecutionChain.getHandler()).getMethod();
}
}
return null;
}
}
成功:
@Aspect
@Component
public class SuccessfulAuthRequestAuditor {
private AuditEventLogger auditEventLogger;
private AuditEventOutcomeMapper mapper;
@Autowired
public SuccessfulAuthRequestAuditor(AuditEventLogger auditEventLogger, AuditEventOutcomeMapper mapper) {
this.auditEventLogger = auditEventLogger;
this.mapper = mapper;
}
@AfterReturning(pointcut = "execution(@com.company.MyAnnotation * *(..)) && @annotation(myAnnotation) && args(request,..)", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result, MyAnnotation myAnnotation, HttpServletRequest request) {
// do logging
}
@AfterThrowing(pointcut = "execution(@com.company.MyAnnotation * *(..)) && @annotation(myAnnotation) && args(request,..)", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Throwable exception, MyAnnotation myAnnotation, HttpServletRequest request) {
// do logging
}
}