【问题标题】:Spring Security WebFlux - "AuthenticationCredentialsNotFoundException: Not Authenticated" with custom AuthenticationEntryPointSpring Security WebFlux - 带有自定义 AuthenticationEntryPoint 的“AuthenticationCredentialsNotFoundException: Not Authenticated”
【发布时间】:2020-01-24 11:35:37
【问题描述】:

我正在尝试使用 Vuejs 前端在反应式 Spring Boot 应用程序中配置 Spring Security,该前端在未经身份验证时将用户重定向到外部 OpenID 提供程序(用于身份验证)。用户通过 OpenID 提供者身份验证并重定向回应用程序(前端)后,将根据 OpenID 提供者的响应创建一个 UsernamePasswordAuthenticationToken(身份验证)并手动进行身份验证。

但是,这样做时,应用程序似乎无法检测到用户已通过身份验证,因为 AuthenticationEntryPoint 仍在调用,但出现以下异常,并且由于未通过身份验证,preAuthenticationFilter(稍后显示)仍被重复调用。

org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: Not Authenticated
    at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.commenceAuthentication(ExceptionTranslationWebFilter.java:72)
    at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.lambda$filter$1(ExceptionTranslationWebFilter.java:44)
    at reactor.core.publisher.Mono.lambda$onErrorResume$25(Mono.java:3146)
    at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:88)
    at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onError(Operators.java:1748)
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:165)
    at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onError(Operators.java:1748)
    at reactor.core.publisher.Operators.error(Operators.java:181)
    at reactor.core.publisher.MonoError.subscribe(MonoError.java:52)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.Mono.subscribe(Mono.java:3852)
    at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:75)
    at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:160)
    at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:98)
    at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:96)
    at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:77)
    at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:275)
    at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:849)
    at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1515)
    at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:241)
    at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1515)
    at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:100)
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:144)
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:144)
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onComplete(FluxFilterFuseable.java:166)
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:189)
    at reactor.core.publisher.MonoFlatMap$FlatMapInner.onComplete(MonoFlatMap.java:260)
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:141)
    at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:114)
    at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1515)
    at reactor.core.publisher.MonoProcessor.subscribe(MonoProcessor.java:457)
    at reactor.core.publisher.MonoMap.subscribe(MonoMap.java:55)
    at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60)
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:113)
    at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2071)
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:185)
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:103)
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:82)
    at reactor.core.publisher.MonoCurrentContext.subscribe(MonoCurrentContext.java:35)
    at reactor.core.publisher.MonoFilterFuseable.subscribe(MonoFilterFuseable.java:47)
    at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60)
    at reactor.core.publisher.MonoFilterFuseable.subscribe(MonoFilterFuseable.java:47)
    at reactor.core.publisher.MonoMapFuseable.subscribe(MonoMapFuseable.java:59)
    at reactor.core.publisher.MonoMapFuseable.subscribe(MonoMapFuseable.java:59)
    at reactor.core.publisher.MonoDefaultIfEmpty.subscribe(MonoDefaultIfEmpty.java:37)
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:113)
    at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2071)
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:185)
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:162)
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:103)
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90)
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:82)
    at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54)
    at reactor.core.publisher.MonoFilterFuseable.subscribe(MonoFilterFuseable.java:47)
    at reactor.core.publisher.MonoMapFuseable.subscribe(MonoMapFuseable.java:59)
    at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60)
    at reactor.core.publisher.Mono.subscribe(Mono.java:3852)
    at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:442)
    at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:212)
    at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:139)
    at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:63)
    at reactor.core.publisher.FluxConcatMap.subscribe(FluxConcatMap.java:121)
    at reactor.core.publisher.MonoNext.subscribe(MonoNext.java:40)
    at reactor.core.publisher.MonoDefaultIfEmpty.subscribe(MonoDefaultIfEmpty.java:37)
    at reactor.core.publisher.MonoFilter.subscribe(MonoFilter.java:46)
    at reactor.core.publisher.MonoSwitchIfEmpty.subscribe(MonoSwitchIfEmpty.java:44)
    at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60)
    at reactor.core.publisher.MonoSwitchIfEmpty.subscribe(MonoSwitchIfEmpty.java:44)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.Mono.subscribe(Mono.java:3852)
    at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172)
    at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56)
    at reactor.core.publisher.Mono.subscribe(Mono.java:3852)
    at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:75)
    at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:160)
    at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:78)
    at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2073)
    at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:1879)
    at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:1753)
    at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54)
    at reactor.core.publisher.Mono.subscribe(Mono.java:3852)
    at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:75)
    at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:96)
    at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:160)
    at reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:794)
    at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:560)
    at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:540)
    at reactor.core.publisher.FluxFlatMap$FlatMapMain.onComplete(FluxFlatMap.java:426)
    at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:265)
    at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:201)
    at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:335)
    at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:139)
    at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:63)
    at reactor.core.publisher.FluxFlatMap.subscribe(FluxFlatMap.java:97)
    at reactor.core.publisher.FluxFilter.subscribe(FluxFilter.java:53)
    at reactor.core.publisher.MonoNext.subscribe(MonoNext.java:40)
    at reactor.core.publisher.MonoSwitchIfEmpty.subscribe(MonoSwitchIfEmpty.java:44)
    at reactor.core.publisher.MonoFilter.subscribe(MonoFilter.java:46)
    at reactor.core.publisher.MonoSwitchIfEmpty.subscribe(MonoSwitchIfEmpty.java:44)
    at reactor.core.publisher.MonoMap.subscribe(MonoMap.java:55)
    at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60)
    at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
    at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1515)
    at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:100)
    at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:136)
    at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:136)
    at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:160)
    at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onComplete(FluxMap.java:262)
    at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1516)
    at reactor.core.publisher.MonoProcessor.subscribe(MonoProcessor.java:457)
    at reactor.core.publisher.MonoMap.subscribe(MonoMap.java:52)
    at reactor.core.publisher.MonoFilter.subscribe(MonoFilter.java:46)
    at reactor.core.publisher.MonoMap.subscribe(MonoMap.java:55)
    at reactor.core.publisher.MonoMap.subscribe(MonoMap.java:55)
    at reactor.core.publisher.MonoDefaultIfEmpty.subscribe(MonoDefaultIfEmpty.java:37)
    at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.Mono.subscribe(Mono.java:3852)
    at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172)
    at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56)
    at reactor.core.publisher.Mono.subscribe(Mono.java:3852)
    at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:75)
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:141)
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:113)
    at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2071)
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:185)
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:103)
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:82)
    at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54)
    at reactor.core.publisher.MonoFilterFuseable.subscribe(MonoFilterFuseable.java:47)
    at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60)
    at reactor.core.publisher.MonoSwitchIfEmpty.subscribe(MonoSwitchIfEmpty.java:44)
    at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.MonoSubscriberContext.subscribe(MonoSubscriberContext.java:47)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.MonoSubscriberContext.subscribe(MonoSubscriberContext.java:47)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)
    at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1515)
    at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:241)
    at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1515)
    at reactor.core.publisher.MonoCollectList$MonoCollectListSubscriber.onComplete(MonoCollectList.java:121)
    at reactor.core.publisher.FluxIterable$IterableSubscription.fastPath(FluxIterable.java:333)
    at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:198)
    at reactor.core.publisher.MonoCollectList$MonoCollectListSubscriber.onSubscribe(MonoCollectList.java:72)
    at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:139)
    at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:63)
    at reactor.core.publisher.MonoCollectList.subscribe(MonoCollectList.java:40)
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
    at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67)
    at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76)
    at reactor.core.publisher.FluxFilterWhen$FluxFilterWhenSubscriber.drain(FluxFilterWhen.java:295)
    at reactor.core.publisher.FluxFilterWhen$FluxFilterWhenSubscriber.onNext(FluxFilterWhen.java:134)
    at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:243)
    at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:201)
    at reactor.core.publisher.FluxFilterWhen$FluxFilterWhenSubscriber.onSubscribe(FluxFilterWhen.java:194)
    at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:139)
    at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:63)
    at reactor.core.publisher.FluxFilterWhen.subscribe(FluxFilterWhen.java:69)
    at reactor.core.publisher.MonoNext.subscribe(MonoNext.java:40)
    at reactor.core.publisher.MonoSwitchIfEmpty.subscribe(MonoSwitchIfEmpty.java:44)
    at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60)
    at reactor.core.publisher.MonoMapFuseable.subscribe(MonoMapFuseable.java:59)
    at reactor.core.publisher.MonoMapFuseable.subscribe(MonoMapFuseable.java:59)
    at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.MonoPeekTerminal.subscribe(MonoPeekTerminal.java:61)
    at reactor.core.publisher.MonoPeekFuseable.subscribe(MonoPeekFuseable.java:74)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
    at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44)
    at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44)
    at reactor.core.publisher.MonoPeekTerminal.subscribe(MonoPeekTerminal.java:61)
    at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44)
    at reactor.core.publisher.Mono.subscribe(Mono.java:3852)
    at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172)
    at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56)
    at reactor.core.publisher.MonoPeekFuseable.subscribe(MonoPeekFuseable.java:70)
    at reactor.core.publisher.MonoPeekTerminal.subscribe(MonoPeekTerminal.java:61)
    at reactor.netty.http.server.HttpServerHandle.onStateChange(HttpServerHandle.java:64)
    at reactor.netty.tcp.TcpServerBind$ChildObserver.onStateChange(TcpServerBind.java:226)
    at reactor.netty.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:442)
    at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:91)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
    at reactor.netty.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:161)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
    at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:438)
    at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:328)
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:302)
    at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1421)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:697)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:632)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:549)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:511)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:918)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:835)
Caused by: org.springframework.security.access.AccessDeniedException: Access Denied
    at org.springframework.security.authorization.ReactiveAuthorizationManager.lambda$verify$1(ReactiveAuthorizationManager.java:53)
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44)
    ... 214 more

到目前为止,我已经设法通过使用以下 AuthenticationEntryPoint 将用户重定向到外部 OpenID 提供程序进行身份验证。如果用户能够登录到 OpenID 提供程序,也能够被重定向回前端。

@Component
public class OpenIDAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
  @Autowired
  private OpenIdUtil openIdUtil;

  @Override
  public Mono<Void> commence(ServerWebExchange serverWebExchange, AuthenticationException e) {
    e.printStackTrace();
    return openIdUtil.authRequest(serverWebExchange); // invoke openid function
  }
}

OpenIdUtil 类(参考 openid4java 的 SampleConsumer)

Log4j2
@Service
public class OpenIdUtil {
  private ConsumerManager manager;

  public OpenIdUtil() {
    // instantiate a ConsumerManager object
    manager = new ConsumerManager();
  }

  /**  placing the authentication request  **/
  public Mono<Void> authRequest(ServerWebExchange serverWebExchange) {
    System.out.println("AUTH-REQUEST");
    try
    {
      // configure the return_to URL where your application will receive the authentication responses from the OpenID provider
      String returnToUrl = "<return url>"; 

      // perform discovery on the user-supplied identifier
      List discoveries = manager.discover("<the url of the external openid provider>");

      // attempt to associate with the OpenID provider and retrieve one service endpoint for authentication
      DiscoveryInformation discovered = manager.associate(discoveries);

      // store the discovery information in the user's session
      serverWebExchange.getSession().map(session -> {
        session.getAttributes().put("openid-disc", discovered);
        return session;
      }).subscribe();

      // obtain a AuthRequest message to be sent to the OpenID provider
      AuthRequest authReq = manager.authenticate(discovered, returnToUrl);

      serverWebExchange.getResponse().setStatusCode(HttpStatus.TEMPORARY_REDIRECT);
      serverWebExchange.getResponse().getHeaders().setLocation(URI.create(authReq.getDestinationUrl(true)));
      return serverWebExchange.getResponse().setComplete();
    }
    catch (OpenIDException e)
    {
      // present error to the user
      e.printStackTrace();
    }
    return null;
  }

  /** processing the authentication response **/
  String verifyResponse(ServerWebExchange serverWebExchange) {

    try
    {
      // extract the parameters from the authentication response (which comes in as a HTTP request from the OpenID provider)
      MultiValueMap<String, String> queryParams = serverWebExchange.getRequest().getQueryParams();
      ParameterList response = new ParameterList(queryParams.toSingleValueMap());

      // retrieve the previously stored discovery information
      final DiscoveryInformation[] discovered = new DiscoveryInformation[1];
      serverWebExchange.getSession().map(session -> discovered[0] = session.getAttribute("openid-disc")).subscribe();

      // extract the receiving URL from the HTTP request
      String receivingURL = serverWebExchange.getRequest().getURI().toString();
      // verify the response; ConsumerManager needs to be the same (static) instance used to place the authentication request
      VerificationResult verification = manager.verify(receivingURL, response, discovered[0]);

      // examine the verification result and extract the verified identifier
      Identifier verified = verification.getVerifiedId();

      if (verified != null)
      {
        return verified.getIdentifier();  // success
      }
    }
    catch (OpenIDException e)
    {
      // present error to the user
    e.printStackTrace();
    }

    return null;
  }

}

我还创建了一个自定义 ReactiveAuthenticationManager 来手动执行“身份验证”(参考 Spring webflux JWT 示例 - https://github.com/heesuk-ahn/spring-webflux-jwt-auth-example

@Component
public class OpenIDReactiveAuthenticationManager implements ReactiveAuthenticationManager {
  @Override
  public Mono<Authentication> authenticate(Authentication authentication) {
    return Mono.just(authentication);
  }
}

还有一个 ServerAuthenticationConverter 来验证来自 AuthenticationFilter ServerWebExchange 的 OpenId 身份验证响应。

@Component
public class PreAuthenticationConverter implements ServerAuthenticationConverter {

  @Autowired
  private OpenIdUtil openIdUtil;

  @Override
  public Mono<Authentication> convert(ServerWebExchange serverWebExchange) {
    try {
      String user = URI.create(openIdUtil.verifyResponse(serverWebExchange)).getPath();
      // extract credentials here
      if (!user.isEmpty()){
        SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
        List<SimpleGrantedAuthority> updatedAuthorities = new ArrayList<>();
        updatedAuthorities.add(authority);
        Authentication authentication = new UsernamePasswordAuthenticationToken(user,null, updatedAuthorities);

        return Mono.just(authentication);
      }
      return Mono.empty();
    } catch (Exception e) {
      // log error here
      return Mono.empty();
    }
  }
}

最后是 SecurityConfiguration 类。

@Configuration
@EnableWebFluxSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {

  @Autowired
  private PreAuthenticationConverter preAuthenticationConverter;

  @Autowired
  private OpenIDAuthenticationEntryPoint openIDAuthenticationEntryPoint;

  @Autowired
  private OpenIDReactiveAuthenticationManager openIDReactiveAuthenticationManager;

  SecurityWebFilterChain securityWebFilterChainStg(ServerHttpSecurity http) {
    return http.cors()
        .and()
        .csrf().disable()
        .authorizeExchange()
        .pathMatchers("/actuator/health", "/actuator/info").permitAll()
        .anyExchange().authenticated()
        .and()
        .addFilterAt(preAuthenticationFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
        .exceptionHandling().authenticationEntryPoint(openIDAuthenticationEntryPoint)
        .and()
        .build();
  }

  @Bean
  CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Collections.singletonList("*"));
    configuration.addAllowedMethod("GET");
    configuration.addAllowedMethod("POST");
    configuration.addAllowedMethod("DELETE");
    configuration.addAllowedHeader("authorization");
    configuration.addAllowedHeader("content-type");
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
  }

  private AuthenticationWebFilter preAuthenticationFilter(){
    AuthenticationWebFilter preAuthenticationFilter = new AuthenticationWebFilter(openIDReactiveAuthenticationManager);
    preAuthenticationFilter.setServerAuthenticationConverter(preAuthenticationConverter);
    return preAuthenticationFilter;
  }
}

我尝试了各种方法,例如手动将身份验证设置到 WebSessionServerSecurityContextRepository 中,但仍然无法对用户进行身份验证,因为 AuthenticationEntryPoint 不断为来自前端的每个请求获取调用。

ServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository();
    return serverSecurityContextRepository.save(exchange,
        new SecurityContextImpl(authentication))
        .subscriberContext(ReactiveSecurityContextHolder.withAuthentication(authentication));

希望有人可以提供一些见解/指南,说明为什么认为身份验证未被“身份验证”,而不是预期的结果,即用户将能够访问前端,而无需通过反复来回重定向前端和 OpenID 提供程序AuthenticationEntryPoint

【问题讨论】:

  • 我认为您需要先了解 oauth2 流程在春季如何工作,然后在了解之后了解 OpenId 如何在春季的 oauth2 流程中使用。 docs.spring.io/spring-security/site/docs/current/reference/… 你在使用 webflux 时也需要编码,你不应该在你的应用程序中使用 subscribe
  • 我也强烈建议您花 30 分钟时间阅读 docs.spring.io/spring-security/site/docs/current/reference/… 的技术概述,以便了解 UserDetailsS​​ervice 的作用、authenticationManager 的作用、过滤器以及如何配置 oauth2 流,所以你不需要编写自己的自定义过滤器。
  • @ThomasAndolf 谢谢,我已经阅读了文档,但如果我错了,请纠正我,因为我对 spring webflux 还很陌生。 oauth2 流中的 OpenID 指的是 OIDC 而不是我正在集成的 OpenID 2.0 协议。因此,这就是我创建一个自定义过滤器的原因,该过滤器将使用以下方式对 Authenticate 创建进行身份验证:Authentication authentication = new UsernamePasswordAuthenticationToken(user,null, updatedAuthorities); 另外,据我了解,在自定义反应式身份验证管理器的身份验证成功后,应自动设置上下文。
  • 我可以看到您对 Spring webflux 还很陌生,因为您不会编写像 serverWebExchange.getSession().map(session -&gt; { session.getAttributes().put("openid-disc", discovered); return session; }).subscribe(); 这样的响应式代码,并且您不会在响应式编程中使用 try catch,因为 try catch 会破坏事件链。然后我还建议你阅读响应式编程的基本原理projectreactor.io/docs/core/release/reference/#intro-reactive
  • 将响应式程序视为一系列回调,或沿途具有不同转换的传送带。订阅者是调用客户端,即网页,当客户端订阅时,它会触发您的事件链(传送带)开始执行。您几乎不应该订阅自己的应用程序。总是有一个发布者(您的服务)和一个订阅者(调用客户端)

标签: spring-boot spring-security openid external spring-webflux


【解决方案1】:

很难知道哪里出了问题,因为我无法运行您的代码,而且您将命令式编程与反应式编程混合在一起。

我在原始问题下方的 cmets 中指出了您的代码存在几个问题。

我将尝试更具体地解释问题所在

public Mono<Void> authRequest(ServerWebExchange serverWebExchange) {

    // Here we are in the imperative world and everything is fine, we get some 
    // stuff and set some parameters.
    System.out.println("AUTH-REQUEST");
    String returnToUrl = "..."; 
    List discoveries = manager.discover( ... );
    DiscoveryInformation discovered = manager.associate(discoveries);

    // Here we now jump into the reactive world, and we start working with Monos. 
    // This means that we have to start chaining things, because we are going to 
    // declare what we want our service to do when we eventually get data in our 
    // Mono.
    return serverWebExchange.getSession()
        .map(session -> {

            AuthRequest authReq = manager.authenticate(discovered, returnToUrl);
            serverWebExchange.getResponse()
                .setStatusCode(HttpStatus.TEMPORARY_REDIRECT);
            serverWebExchange.getResponse()
                .getHeaders()
                .setLocation(URI.create(authReq.getDestinationUrl(true)));      

            session.getAttributes().put("openid-disc", discovered);
        return serverWebExchange.getResponse().setComplete();
    })
}

我希望你通过这个例子更清楚地理解这个概念。

【讨论】:

  • 谢谢!我将以此为起点来理解并将代码更改为反应式编程。一旦我更新了代码并测试代码是否有效,我将更新帖子。
猜你喜欢
  • 2016-11-13
  • 2011-12-22
  • 1970-01-01
  • 2011-07-08
  • 2018-05-25
  • 2017-05-17
  • 2018-10-05
  • 1970-01-01
  • 2019-10-10
相关资源
最近更新 更多