java Spring AOP with Spring Webflux after authentication

hyrbngr7  于 2023-04-28  发布在  Java
关注(0)|答案(1)|浏览(169)

我有一个启用的Spring AOP用于我的日志记录目的,并使用webflux作为API入口点,如果api不像我的login api那样通过ReactiveAuthenticationManager,事情就可以正常工作,但当它必须通过ReactiveAuthenticationManager时,它会抛出下面的异常

java.lang.IllegalStateException: No MethodInvocation found: Check that an AOP invocation is in progress and that the ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor! In addition, ExposeInvocationInterceptor and ExposeInvocationInterceptor.currentInvocation() must be invoked from the same thread.
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.currentInvocation(ExposeInvocationInterceptor.java:74) ~[spring-aop-5.3.23.jar:5.3.23]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    *__checkpoint ⇢ Handler com.ums.api.contoller.Controller#getAllUser() [DispatcherHandler]

我的AOP类

public class LoggerAspect {

    @Pointcut("within(com.ums..*)")
    public void logEveryFunction() {
    }

    @Before(value = "logEveryFunction()")
    public void log(JoinPoint joinPoint) {
        log.info("Entering function {} in location {}", joinPoint.getSignature(), joinPoint.getSourceLocation());
    }

    @After(value = "logEveryFunction()")
    public void logEnd(JoinPoint joinPoint) {
        log.info("Exiting function {}", joinPoint.getSignature());
    }
}

认证功能

public Mono<Authentication> authenticate(Authentication authentication) {
        return Mono.justOrEmpty(authentication.getCredentials().toString())
                .map(value -> {
                    return new UsernamePasswordAuthenticationToken(
                            authentication.getName(),
                            null,
                          Arrays.asList(new SimpleGrantedAuthority("VIEW"))
                });
    }

如何使我的AOP与webflux一起工作?如何解决这个问题?

u5i3ibmn

u5i3ibmn1#

让我们再次阅读错误信息:

java.lang.IllegalStateException: No MethodInvocation found:

Check that an AOP invocation is in progress and that the
ExposeInvocationInterceptor is upfront in the interceptor chain.

Specifically, note that advices with order HIGHEST_PRECEDENCE will execute
before ExposeInvocationInterceptor! In addition, ExposeInvocationInterceptor
and ExposeInvocationInterceptor.currentInvocation() must be invoked from the
same thread.

所以让我们尝试在方面添加一个@Order注解。但首先,一些研究。你可以检查Javadocs,我只是检查了源代码:

public @interface Order {
  int value() default Ordered.LOWEST_PRECEDENCE;
}
public interface Ordered {
  int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
  int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

  int getOrder();
}

结论:默认顺序为LOWEST_PRECEDENCE,i。即Integer.MAX_VALUE。因此,让我们分配一个更高的优先级,即。即小于Integer.MAX_VALUE的东西。对于GitHub上的简单示例,以下任何一个都可以:@Order(Integer.MAX_VALUE - 1)@Order(0)或任何您认为适合您情况的内容。将其添加到aspect类后,调用者将在控制台上看到以下内容:

$ curl -L "http://localhost:8080/login" -H "Authorization: Bearer ghfjhgf"
HI

Spring服务器日志会说:

[  restartedMain] c.a.s.SpringBootWebfluxJjwtApplication   : Started SpringBootWebfluxJjwtApplication in 1.646 seconds (JVM running for 2.156)
[  restartedMain] o.s.b.a.ApplicationAvailabilityBean      : Application availability state LivenessState changed to CORRECT
[  restartedMain] o.s.b.a.ApplicationAvailabilityBean      : Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
...
[oundedElastic-1] c.a.s.rest.LoggerAspect                  : Entering function Mono com.ard333.springbootwebfluxjjwt.rest.AuthenticationREST.login() in location org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$SourceLocationImpl@641fcca3
[oundedElastic-1] c.a.s.rest.LoggerAspect                  : Exiting function Mono com.ard333.springbootwebfluxjjwt.rest.AuthenticationREST.login()

更新,回应此后续问题:

我仍然想知道为什么会发生这种情况,为什么需要@Order?只有一个建议(从我的AOP),其他将运行?
如果您从返回Mono的登录方法中删除@PreAuthorize,那么它在方面没有@Order注解的情况下也可以工作。这给了你一个线索:SpringSecurity似乎也使用了AOP或者至少是某种方法拦截器。让我们更详细地看看实际错误:

java.lang.IllegalStateException: No MethodInvocation found: Check that an AOP invocation is in progress and that the ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor! In addition, ExposeInvocationInterceptor and ExposeInvocationInterceptor.currentInvocation() must be invoked from the same thread.
  at org.springframework.aop.interceptor.ExposeInvocationInterceptor.currentInvocation(ExposeInvocationInterceptor.java:74) ~[spring-aop-5.3.7.jar:5.3.7]
  Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
  |_ checkpoint ⇢ Handler com.ard333.springbootwebfluxjjwt.rest.AuthenticationREST#login() [DispatcherHandler]
  |_ checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
  |_ checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
  |_ checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
  |_ checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
  |_ checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
  |_ checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
  |_ checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
  |_ checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
  |_ checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
  |_ checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
  |_ checkpoint ⇢ HTTP GET "/login" [ExceptionHandlingWebHandler]
Stack trace:
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.currentInvocation(ExposeInvocationInterceptor.java:74) ~[spring-aop-5.3.7.jar:5.3.7]
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.getJoinPointMatch(AbstractAspectJAdvice.java:658) ~[spring-aop-5.3.7.jar:5.3.7]
    at org.springframework.aop.aspectj.AspectJMethodBeforeAdvice.before(AspectJMethodBeforeAdvice.java:44) ~[spring-aop-5.3.7.jar:5.3.7]
    at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:57) ~[spring-aop-5.3.7.jar:5.3.7]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.7.jar:5.3.7]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.7.jar:5.3.7]
    at org.springframework.security.access.prepost.PrePostAdviceReactiveMethodInterceptor.proceed(PrePostAdviceReactiveMethodInterceptor.java:156) ~[spring-security-core-5.5.0.jar:5.5.0]
    at org.springframework.security.access.prepost.PrePostAdviceReactiveMethodInterceptor.lambda$invoke$4(PrePostAdviceReactiveMethodInterceptor.java:116) ~[spring-security-core-5.5.0.jar:5.5.0]
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:125) ~[reactor-core-3.4.6.jar:3.4.6]
(...)

请注意堆栈跟踪中的org.springframework.security.access.prepost.PrePostAdviceReactiveMethodInterceptor。显然,在Spring Security中,有一个用于响应式方法的特殊方法拦截器。正如你和我之前发布的错误消息中所描述的,这个拦截器似乎需要拦截器/建议链的特殊排序。我猜,这是某种 Bootstrap 问题,希望合并Spring AOP,Spring Security和WebFlux的用户需要适应。

相关问题