Spring Integration is hitting mutliple outbound requests even the request is failing - spring-integration-dsl

#Bean
public IntegrationFlow initiateFlowGeocode3() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(requestFactory());
System.out.println("**********in flow3");
return IntegrationFlows.from("FlowChannel2").transform(Transformers.toJson()).log("IN GATEWAY 3 !").handle(
Http.outboundGateway("URL",
restTemplate).charset("UTF-8").httpMethod(HttpMethod.GET).expectedResponseType(String.class)
.errorHandler(new MyResponseErrorHandler()))
.log("after the rest api call3 ").transform(new JsonToObjectTransformer(RestClientBean.class)).get();
}
#Bean
public IntegrationFlow errorResponse() {
return IntegrationFlows.from(renewalErrorChannel()).log("ERROR FLOW TRIGGERED").get();
}
#Bean
public PollableChannel renewalErrorChannel() {
return new QueueChannel();
}
is continuously getting hit. If there's a failure, the flow should stop hitting it, but it's continuously polling the failure request. Can the polling request be stopped?

Related

spring security - authorize pre-flighted request without oAuth token

I am trying to authorize all preflight request in (/secure/**) without an authorization header(oauth token in my case). The JwkFilter is used to validate the oauth token passed in the authorization header. Any suggestion, where I am going wrong here.
#Override
protected void configure(HttpSecurity http) throws Exception {
JwtAuthFilter jwtAuthTokenFilter = new JwtAuthFilter(oauthConfig);
jwtAuthTokenFilter.setAuthenticationManager(getAuthManager());
http.cors().and().authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/secure/**")
.permitAll();
http.requiresChannel().anyRequest().requiresSecure().and()
.addFilterBefore(requireProtocolFilter, ChannelProcessingFilter.class).sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().anonymous().disable().csrf().disable()
.antMatcher("/**").authorizeRequests().anyRequest().permitAll().and()
.antMatcher(/secure/**")
.addFilterBefore(jwtAuthTokenFilter, BasicAuthenticationFilter.class).exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint()).and().authorizeRequests().anyRequest()
.authenticated();
}
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("*")
.allowedOrigins("*");
}
};
}
For preflight request with CORS, according to spring, they will execute before your jwtAuthTokenFilter (registered before BasicAuthenticationFilter filter) -> correct
The order was specified here (in spring code):
FilterComparator() {
Step order = new Step(INITIAL_ORDER, ORDER_STEP);
...
put(CorsFilter.class, order.next());
...
put(BasicAuthenticationFilter.class, order.next());
...
}
In CORS, for complex request (like using custom header Authorization header in your case), browser will send preflight request first to know whether the server allow client to access their resource or not before sending actual request.
The CORSFilter will execute like this (in spring code):
public class CorsFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(request);
boolean isValid = this.processor.processRequest(corsConfiguration, request, response);
if (!isValid || CorsUtils.isPreFlightRequest(request)) {
return;
}
filterChain.doFilter(request, response);
}
}
They will check whether for every preflight request (extends OncePerRequestFilter) comes to server, if processRequest is valid or is preflight request to terminate the chain.
Here is the default processor to check preflight request (in spring code):
public class DefaultCorsProcessor implements CorsProcessor {
#Override
public boolean processRequest(#Nullable CorsConfiguration config, HttpServletRequest request,
HttpServletResponse response) throws IOException {
...
boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);
if (config == null) {
if (preFlightRequest) {
rejectRequest(new ServletServerHttpResponse(response));
return false;
}
else {
return true;
}
}
return handleInternal(new ServletServerHttpRequest(request), new ServletServerHttpResponse(response), config, preFlightRequest);
}
In your case, I think you are missing configuring for enabling CORS.
So the server reject the client request (by sending HttpStatus.FORBIDDEN code), so that the browser don't send actual request to the server.
And your JwtAuthTokenFilter has no chance to execute.
You can refer to this post for configuring cors. Hope it helps
Adding the below snippet in to the jwkAuthFilter did the trick.
if (CorsUtils.isPreFlightRequest(request)) {
response.setStatus(HttpServletResponse.SC_OK);
return;
}

How to preauthorize access to a Http.InboundGateway?

I know its possible to add a #PreAuthorize annotation to a Rest Controller...
#RestController
public class WebController {
#PreAuthorize("hasAuthority('Foo')")
#GetMapping("/restricted")
public ResponseEntity<String> restricted() {
return ResponseEntity.ok("Restricted section");
}
}
How can one preauthorize access to a Spring Integration Http.inbound gateway? I know I could add in a component to the Integration flow and add the annotation on a transformer or service activator method but I'd rather not have a separate object for that.
#Bean
//#PreAuthorize("hasAuthority('Foo')") ?
public HttpRequestHandlingMessagingGateway restrictedGateway() {
return Http.inboundGateway("/restricted")
...
.get();
}
#Bean
public IntegrationFlow myFlow(HttpRequestHandlingMessagingGateway restrictedGateway) {
return IntegrationFlows
.from(restrictedGateway)
.transform(source -> "Restricted section")
.get();
}
I think you are right by looking at https://docs.spring.io/spring-integration/reference/html/security.htm where it allows channel to be declared #Secured
Even if we think about spring security on a normal spring boot app without integration, it is at filter level so it seems to make sense as I consider HttpRequestHandlingMessagingGateway as a listener for http requests
Can you try
#Bean
#SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = "ROLE_XXX")
public SubscribableChannel secureChannel() {
return new DirectChannel();
}
#Bean
public IntegrationFlow myFlow(HttpRequestHandlingMessagingGateway
restrictedGateway) {
return IntegrationFlows
.from(restrictedGateway)
.channel(secureChannel())
.transform(source -> "Restricted section")
.get();
}

Spring Webflux Websocket Security - Basic Authentication

PROBLEM: I am not getting Spring Security with Websockets to work in a Webflux project.
NOTE: I am using Kotlin instead of Java.
DEPENDENCIES:
Spring Boot 2.0.0
Spring Security 5.0.3
Spring WebFlux 5.0.4
IMPORTANT UPDATE: I have raised a Spring Issue bug (March 30) here and one of the Spring security maintainers said its NOT SUPPORTED but they can add it for Spring Security 5.1.0 M2.
LINK: Add WebFlux WebSocket Support #5188
Webflux Security Configuration
#EnableWebFluxSecurity
class SecurityConfig
{
#Bean
fun configure(http: ServerHttpSecurity): SecurityWebFilterChain
{
return http.authorizeExchange()
.pathMatchers("/").permitAll()
.anyExchange().authenticated()
.and().httpBasic()
.and().formLogin().disable().csrf().disable()
.build()
}
#Bean
fun userDetailsService(): MapReactiveUserDetailsService
{
val user = User.withDefaultPasswordEncoder()
.username("user")
.password("pass")
.roles("USER")
.build()
return MapReactiveUserDetailsService(user)
}
}
Webflux Websocket Configuration
#Configuration
class ReactiveWebSocketConfiguration
{
#Bean
fun webSocketMapping(handler: WebSocketHandler): HandlerMapping
{
val map = mapOf(Pair("/event", handler))
val mapping = SimpleUrlHandlerMapping()
mapping.order = -1
mapping.urlMap = map
return mapping
}
#Bean
fun handlerAdapter() = WebSocketHandlerAdapter()
#Bean
fun websocketHandler() = WebSocketHandler { session ->
// Should print authenticated principal BUT does show NULL
println("${session.handshakeInfo.principal.block()}")
// Just for testing we send hello world to the client
session.send(Mono.just(session.textMessage("hello world")))
}
}
Client Code
// Lets create a websocket and pass Basic Auth to it
new WebSocket("ws://user:pass#localhost:8000/event");
// ...
Obserservations
In the websocket handler the principal shows null
The client can connect without being authenticated. If I do WebSocket("ws://localhost:8000/event") without the Basic Auth it stills works! So Spring Security does not authenticate anything.
What I am missing?
What I do wrong?
I could advise you to implement your own authentication mechanism instead of exploiting Spring Security.
When WebSocket connection is about to establish it uses handshake mechanism accompanied by an UPGRADE request. Base on that, our idea would be to use our own handler for the request and perform authentication there.
Fortunately, Spring Boot has RequestUpgradeStrategy for such purpose. On top of that, based on the application server what you use, Spring provides a default implementation of those strategies. As I use Netty bellow the class would be ReactorNettyRequestUpgradeStrategy.
Here is the suggested prototype:
/**
* Based on {#link ReactorNettyRequestUpgradeStrategy}
*/
#Slf4j
#Component
public class BasicAuthRequestUpgradeStrategy implements RequestUpgradeStrategy {
private int maxFramePayloadLength = NettyWebSocketSessionSupport.DEFAULT_FRAME_MAX_SIZE;
private final AuthenticationService service;
public BasicAuthRequestUpgradeStrategy(AuthenticationService service) {
this.service = service;
}
#Override
public Mono<Void> upgrade(ServerWebExchange exchange, //
WebSocketHandler handler, //
#Nullable String subProtocol, //
Supplier<HandshakeInfo> handshakeInfoFactory) {
ServerHttpResponse response = exchange.getResponse();
HttpServerResponse reactorResponse = getNativeResponse(response);
HandshakeInfo handshakeInfo = handshakeInfoFactory.get();
NettyDataBufferFactory bufferFactory = (NettyDataBufferFactory) response.bufferFactory();
String originHeader = handshakeInfo.getHeaders()
.getOrigin();// you will get ws://user:pass#localhost:8080
return service.authenticate(originHeader)//returns Mono<Boolean>
.filter(Boolean::booleanValue)// filter the result
.doOnNext(a -> log.info("AUTHORIZED"))
.flatMap(a -> reactorResponse.sendWebsocket(subProtocol, this.maxFramePayloadLength, (in, out) -> {
ReactorNettyWebSocketSession session = //
new ReactorNettyWebSocketSession(in, out, handshakeInfo, bufferFactory, this.maxFramePayloadLength);
return handler.handle(session);
}))
.switchIfEmpty(Mono.just("UNATHORIZED")
.doOnNext(log::info)
.then());
}
private static HttpServerResponse getNativeResponse(ServerHttpResponse response) {
if (response instanceof AbstractServerHttpResponse) {
return ((AbstractServerHttpResponse) response).getNativeResponse();
} else if (response instanceof ServerHttpResponseDecorator) {
return getNativeResponse(((ServerHttpResponseDecorator) response).getDelegate());
} else {
throw new IllegalArgumentException("Couldn't find native response in " + response.getClass()
.getName());
}
}
}
Moreover, if you do not have crucial logical dependencies onto Spring Security in the project such as complex ACL logic, then I advise you to get rid of it and even do not use it at all.
The reason for that is that I see Spring Security as a violator of the reactive approach due to its, I would say, MVC legacy mindset. It entangles your application with tons of extra configurations and "not-on-the-surface" tunings and forces engineers to maintain those configurations, making them more and more complex. In most cases, things could be implemented very smoothly without touching Spring Security at all. Just create a component and use it in a proper way.
Hope it helps.

null principal returned by ServerRequest in webflux request handler

I have set up authentication in a Spring WebFlux application. The authentication mechanism appears to work fine. For example the following code is used to set up security web filter chain:
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http.authorizeExchange()
.pathMatchers("/path/to/resource").hasAuthority("A_ROLE")
.anyExchange().authenticated()
.and().httpBasic()
.and().build();
}
This works as expected in conjunction with the UserDetailsRepositoryReactiveAuthenticationManager and MapReactiveUserDetailsService. If a user doesn't have the required authority a forbidden error code is returned and otherwise the request is passed on to the handler.
I have a requirement to apply fine grained permission checks within the handler itself and figured that I should be able to retrieve the authorities from the request as follows:
public Mono<ServerResponse> getMyResource(ServerRequest serverRequest) {
Authentication authentication = (Authentication)serverRequest.principal().block();
...
}
However, I find that the principal is always null. First, is this the correct way to get a handle on the authorities, and if so is there possibly some upstream configuration I'm missing?
You are blocking the result before is available. You can simply flatmap it so that you don't have to block it.
public Mono<ServerResponse> getMyResource(ServerRequest serverRequest) {
return serverRequest.principal().flatMap((principal) -> ServerResponse.ok()
.body(fromObject("Hello " + principal.getName())));
}
UPDATE: If you want to retrieve the principal and body you could zip them.
public Mono<ServerResponse> getMyResource(ServerRequest serverRequest) {
return Mono.zip(
serverRequest.principal(),
serverRequest.bodyToMono(String.class)
).flatMap(tuple -> {
Principal principal = tuple.getT1();
String body = tuple.getT2();
return ServerResponse.ok().build();
});
}

How to secure reactor netServer with spring security?

I try to develop an "hybrid" server using spring boot webApplication with embedded tomcat and a netServer from reactor to scale-up my Rest Api.
There are no Spring controller, all the incoming request are handled by the netServer.
Never the less i'd like to have a login page using spring security remember me facilities to authenticate the user and use this authentication to secure incoming request on the reactor netServer.
I start to implements the netServer, according to this tutorial reactor thumbmailer
here is my netServer :
NetServer<FullHttpRequest, FullHttpResponse> server = new TcpServerSpec<FullHttpRequest, FullHttpResponse>(NettyTcpServer.class)
.env(env)
.dispatcher("sync")
.listen(8080)
.options(opts)
.consume(ch -> {
// attach an error handler
ch.when(Throwable.class, UserController.errorHandler(ch));
// filter requests by URI
Stream<FullHttpRequest> in = ch.in();
// serve image thumbnail to browser
in.filter((FullHttpRequest req) -> req.getUri().startsWith(UserController.GET_USER_PROFILE))
.consume(UserController.getUserProfile(ch));
})
.get();
So when a user try to load his profile, the incoming request is handled by the userController :
public static Consumer<FullHttpRequest> getUserProfile(NetChannel<FullHttpRequest, FullHttpResponse> channel) {
UserService userService = StaticContextAccessor.getBean(UserService.class);
return req -> {
try {
LoginDTO login = RestApiUtils.parseJson(LoginDTO.class, RestApiUtils.getJsonContent(req));
DefaultFullHttpResponse resp = new DefaultFullHttpResponse(HTTP_1_1, OK);
String result = userService.loadUserProfile(login);
resp.headers().set(CONTENT_TYPE, "application/json");
resp.headers().set(CONTENT_LENGTH, result.length());
resp.content().writeBytes(result.getBytes());
channel.send(resp);
} catch (Exception e) {
channel.send(badRequest(e.getMessage()));
}
};
}
Here is the hack : getUserProfile is a static methode, so i can't use GlobalMethodSecurity to secure it.
i then inject a userService in this controller using a StaticContextAccessor :
#Component
public class StaticContextAccessor {
private static StaticContextAccessor instance;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void registerInstance() {
instance = this;
}
public static <T> T getBean(Class<T> clazz) {
return instance.applicationContext.getBean(clazz);
}
}
UserService :
#Service
#PreAuthorize("true")
public class UserServiceImpl implements UserService{
public String loadUserProfile(LoginDTO login){
//TODO load profile in mongo
return new GsonBuilder().create().toJson(login);
}
}
the service is managed by spring so i guess i could use spring GlobalMethodSecurity on it (i m still developping this part, but i'm not sure this is the best way to secure my netServer)
Is there a easier way to use Spring security on reactor netServer ???
My first web site version was developped with nodeJS to handle many concurent users, and i try to refactor it using a JVM nio solution.
So is spring / reactor / netty a good solution to have a highly scalable server, or should i use something like play! or vertx.io ?
Thank you so much
Have you tried bootstrapping your NetServer from within a JavaConfig #Bean method? Something like:
#Configuration
#EnableReactor
class AppConfig {
public Function<NetChannel, UserController> users() {
return new UserControllerFactory();
}
#Bean
public NetServer netServer(Environment env, Function<NetChannel, UserController> users) {
return new TcpServerSpec(NettyTcpServer.class)
.env(env)
.dispatcher("sync")
.listen(8080)
.options(opts)
.consume(ch -> {
// attach an error handler
ch.when(Throwable.class, UserController.errorHandler(ch));
// filter requests by URI
Stream<FullHttpRequest> in = ch.in();
// serve image thumbnail to browser
in.filter((FullHttpRequest req) -> req.getUri().startsWith(UserController.GET_USER_PROFILE))
.consume(users.apply(ch));
})
.get();
}
}
This should preserve your Spring Security support and enable you to share handlers as beans rather than as return values from static methods. In general, just about everything you need to do in a Reactor TCP app can be done using beans and injection and by returing the NetServer as a bean itself.

Resources