|
73 | 73 | import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter; |
74 | 74 | import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenAuthenticationConverter; |
75 | 75 | import org.springframework.security.web.server.SecurityWebFilterChain; |
| 76 | +import org.springframework.security.web.server.WebFilterExchange; |
76 | 77 | import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint; |
77 | 78 | import org.springframework.security.web.server.authentication.ServerAuthenticationConverter; |
78 | 79 | import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler; |
| 80 | +import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler; |
79 | 81 | import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler; |
80 | 82 | import org.springframework.test.web.reactive.server.WebTestClient; |
81 | 83 | import org.springframework.web.bind.annotation.GetMapping; |
@@ -371,6 +373,78 @@ public void getWhenUsingCustomAuthenticationFailureHandlerThenUsesIsAccordingly( |
371 | 373 | verify(handler).onAuthenticationFailure(any(), any()); |
372 | 374 | } |
373 | 375 |
|
| 376 | + @Test |
| 377 | + public void getWhenUsingCustomAuthenticationSuccessHandlerThenUsesIsAccordingly() { |
| 378 | + this.spring.register(CustomAuthenticationSuccessHandlerAuthenticationManagerResolverConfig.class).autowire(); |
| 379 | + ServerAuthenticationSuccessHandler handler = this.spring.getContext() |
| 380 | + .getBean(ServerAuthenticationSuccessHandler.class); |
| 381 | + ReactiveAuthenticationManager authenticationManager = this.spring.getContext() |
| 382 | + .getBean(ReactiveAuthenticationManager.class); |
| 383 | + given(authenticationManager.authenticate(any())) |
| 384 | + .willAnswer(input -> Mono.just(input.getArgument(0, Authentication.class))); |
| 385 | + given(handler.onAuthenticationSuccess(any(), any())).willAnswer(input -> { |
| 386 | + WebFilterExchange webFilterExchange = input.getArgument(0, WebFilterExchange.class); |
| 387 | + return webFilterExchange.getChain().filter(webFilterExchange.getExchange()); |
| 388 | + }); |
| 389 | + // @formatter:off |
| 390 | + this.client.get() |
| 391 | + .headers((headers) -> headers.setBearerAuth(this.messageReadToken)) |
| 392 | + .exchange() |
| 393 | + .expectStatus().isUnauthorized(); |
| 394 | + // @formatter:on |
| 395 | + verify(handler).onAuthenticationSuccess(any(), any()); |
| 396 | + } |
| 397 | + |
| 398 | + @Test |
| 399 | + public void getWhenUsingCustomAuthenticationSuccessHandlerWithJwtThenUsesIsAccordingly() { |
| 400 | + this.spring.register(CustomAuthenticationSuccessHandlerJwtConfig.class).autowire(); |
| 401 | + ServerAuthenticationSuccessHandler handler = this.spring.getContext() |
| 402 | + .getBean(ServerAuthenticationSuccessHandler.class); |
| 403 | + ReactiveAuthenticationManager authenticationManager = this.spring.getContext() |
| 404 | + .getBean(ReactiveAuthenticationManager.class); |
| 405 | + given(authenticationManager.authenticate(any())) |
| 406 | + .willAnswer(input -> Mono.just(input.getArgument(0, Authentication.class))); |
| 407 | + given(handler.onAuthenticationSuccess(any(), any())).willAnswer(input -> { |
| 408 | + WebFilterExchange webFilterExchange = input.getArgument(0, WebFilterExchange.class); |
| 409 | + return webFilterExchange.getChain().filter(webFilterExchange.getExchange()); |
| 410 | + }); |
| 411 | + // @formatter:off |
| 412 | + this.client.get() |
| 413 | + .headers((headers) -> headers.setBearerAuth(this.messageReadToken)) |
| 414 | + .exchange() |
| 415 | + .expectStatus().isUnauthorized(); |
| 416 | + // @formatter:on |
| 417 | + verify(handler).onAuthenticationSuccess(any(), any()); |
| 418 | + } |
| 419 | + |
| 420 | + @Test |
| 421 | + public void getWhenUsingCustomAuthenticationSuccessHandlerWIthOpaqueTokenThenUsesIsAccordingly() { |
| 422 | + this.spring.register(CustomAuthenticationSuccessHandlerOpaqueTokenConfig.class, RootController.class).autowire(); |
| 423 | + this.spring.getContext() |
| 424 | + .getBean(MockWebServer.class) |
| 425 | + .setDispatcher(requiresAuth(this.clientId, this.clientSecret, this.active)); |
| 426 | + ServerAuthenticationSuccessHandler handler = this.spring.getContext() |
| 427 | + .getBean(ServerAuthenticationSuccessHandler.class); |
| 428 | + ReactiveAuthenticationManager authenticationManager = this.spring.getContext() |
| 429 | + .getBean(ReactiveAuthenticationManager.class); |
| 430 | + given(authenticationManager.authenticate(any())) |
| 431 | + .willAnswer(input -> Mono.just(input.getArgument(0, Authentication.class))); |
| 432 | + given(handler.onAuthenticationSuccess(any(), any())).willAnswer(input -> { |
| 433 | + WebFilterExchange webFilterExchange = input.getArgument(0, WebFilterExchange.class); |
| 434 | + return webFilterExchange.getChain().filter(webFilterExchange.getExchange()); |
| 435 | + }); |
| 436 | + // @formatter:off |
| 437 | + this.client.get() |
| 438 | + .headers((headers) -> headers |
| 439 | + .setBearerAuth(this.messageReadToken) |
| 440 | + ) |
| 441 | + .exchange() |
| 442 | + .expectStatus().isOk(); |
| 443 | + // @formatter:on |
| 444 | + |
| 445 | + verify(handler).onAuthenticationSuccess(any(), any()); |
| 446 | + } |
| 447 | + |
374 | 448 | @Test |
375 | 449 | public void postWhenSignedThenReturnsOk() { |
376 | 450 | this.spring.register(PublicKeyConfig.class, RootController.class).autowire(); |
@@ -950,6 +1024,110 @@ ServerAuthenticationFailureHandler authenticationFailureHandler() { |
950 | 1024 |
|
951 | 1025 | } |
952 | 1026 |
|
| 1027 | + @Configuration |
| 1028 | + @EnableWebFlux |
| 1029 | + @EnableWebFluxSecurity |
| 1030 | + static class CustomAuthenticationSuccessHandlerAuthenticationManagerResolverConfig { |
| 1031 | + |
| 1032 | + @Bean |
| 1033 | + SecurityWebFilterChain springSecurity(ServerHttpSecurity http) { |
| 1034 | + // @formatter:off |
| 1035 | + http |
| 1036 | + .authorizeExchange((authorize) -> authorize.anyExchange().authenticated()) |
| 1037 | + .oauth2ResourceServer((oauth2) -> oauth2 |
| 1038 | + .authenticationSuccessHandler(authenticationSuccessHandler()) |
| 1039 | + .authenticationManagerResolver(exchange -> Mono.just(authenticationManager())) |
| 1040 | + ); |
| 1041 | + // @formatter:on |
| 1042 | + return http.build(); |
| 1043 | + } |
| 1044 | + |
| 1045 | + @Bean |
| 1046 | + ReactiveAuthenticationManager authenticationManager() { |
| 1047 | + return mock(ReactiveAuthenticationManager.class); |
| 1048 | + } |
| 1049 | + |
| 1050 | + @Bean |
| 1051 | + ServerAuthenticationSuccessHandler authenticationSuccessHandler() { |
| 1052 | + return mock(ServerAuthenticationSuccessHandler.class); |
| 1053 | + } |
| 1054 | + |
| 1055 | + } |
| 1056 | + |
| 1057 | + @Configuration |
| 1058 | + @EnableWebFlux |
| 1059 | + @EnableWebFluxSecurity |
| 1060 | + static class CustomAuthenticationSuccessHandlerJwtConfig { |
| 1061 | + |
| 1062 | + @Bean |
| 1063 | + SecurityWebFilterChain springSecurity(ServerHttpSecurity http) { |
| 1064 | + // @formatter:off |
| 1065 | + http |
| 1066 | + .authorizeExchange((authorize) -> authorize.anyExchange().authenticated()) |
| 1067 | + .oauth2ResourceServer((oauth2) -> oauth2 |
| 1068 | + .authenticationSuccessHandler(authenticationSuccessHandler()) |
| 1069 | + .jwt((jwt) -> jwt.authenticationManager(authenticationManager())) |
| 1070 | + ); |
| 1071 | + // @formatter:on |
| 1072 | + return http.build(); |
| 1073 | + } |
| 1074 | + |
| 1075 | + @Bean |
| 1076 | + ReactiveAuthenticationManager authenticationManager() { |
| 1077 | + return mock(ReactiveAuthenticationManager.class); |
| 1078 | + } |
| 1079 | + |
| 1080 | + @Bean |
| 1081 | + ServerAuthenticationSuccessHandler authenticationSuccessHandler() { |
| 1082 | + return mock(ServerAuthenticationSuccessHandler.class); |
| 1083 | + } |
| 1084 | + |
| 1085 | + } |
| 1086 | + |
| 1087 | + @Configuration |
| 1088 | + @EnableWebFlux |
| 1089 | + @EnableWebFluxSecurity |
| 1090 | + static class CustomAuthenticationSuccessHandlerOpaqueTokenConfig { |
| 1091 | + |
| 1092 | + private MockWebServer mockWebServer = new MockWebServer(); |
| 1093 | + |
| 1094 | + @Bean |
| 1095 | + SecurityWebFilterChain springSecurity(ServerHttpSecurity http) { |
| 1096 | + String introspectionUri = mockWebServer().url("/introspect").toString(); |
| 1097 | + // @formatter:off |
| 1098 | + http |
| 1099 | + .authorizeExchange((authorize) -> authorize.anyExchange().authenticated()) |
| 1100 | + .oauth2ResourceServer((oauth2) -> oauth2 |
| 1101 | + .authenticationSuccessHandler(authenticationSuccessHandler()) |
| 1102 | + .opaqueToken((opaqueToken) -> opaqueToken |
| 1103 | + .introspectionUri(introspectionUri) |
| 1104 | + .introspectionClientCredentials("client", "secret")) |
| 1105 | + ); |
| 1106 | + // @formatter:on |
| 1107 | + return http.build(); |
| 1108 | + } |
| 1109 | + |
| 1110 | + @Bean |
| 1111 | + ReactiveAuthenticationManager authenticationManager() { |
| 1112 | + return mock(ReactiveAuthenticationManager.class); |
| 1113 | + } |
| 1114 | + |
| 1115 | + @Bean |
| 1116 | + ServerAuthenticationSuccessHandler authenticationSuccessHandler() { |
| 1117 | + return mock(ServerAuthenticationSuccessHandler.class); |
| 1118 | + } |
| 1119 | + |
| 1120 | + @Bean |
| 1121 | + MockWebServer mockWebServer() { |
| 1122 | + return this.mockWebServer; |
| 1123 | + } |
| 1124 | + |
| 1125 | + @PreDestroy |
| 1126 | + void shutdown() throws IOException { |
| 1127 | + this.mockWebServer.shutdown(); |
| 1128 | + } |
| 1129 | + } |
| 1130 | + |
953 | 1131 | @EnableWebFlux |
954 | 1132 | @EnableWebFluxSecurity |
955 | 1133 | static class CustomBearerTokenServerAuthenticationConverter { |
|
0 commit comments