Spring Boot should autoconfigure RSocket Security
- It should automatically apply
@EnableRSocketSecurity
- Conditionally create a
MapReactiveUserDetailsService
with secure random password as it does for WebFlux - It should automatically apply Spring Security to
ServerRSocketFactory
@Bean
ServerRSocketFactoryCustomizer springSecurityRSocketSecurity(SecuritySocketAcceptorInterceptor interceptor) {
return builder -> builder.addSocketAcceptorPlugin(interceptor);
}
Comment From: rwinch
Another thing that would be nice for this is if it configured the AuthenticationPrincipalArgumentResolver
and the CurrentSecurityContextArgumentResolver
. It would look something like this:
@Bean
RSocketMessageHandler messageHandler(RSocketStrategies rSocketStrategies, BeanFactory beanFactory) {
BeanFactoryResolver resolver = new BeanFactoryResolver(beanFactory);
AuthenticationPrincipalArgumentResolver principal = new AuthenticationPrincipalArgumentResolver();
principal.setBeanResolver(resolver);
CurrentSecurityContextArgumentResolver context = new CurrentSecurityContextArgumentResolver();
context.setBeanResolver(resolver);
RSocketMessageHandler messageHandler = new RSocketMessageHandler();
messageHandler.setRSocketStrategies(rSocketStrategies);
messageHandler.setRouteMatcher(new PathPatternRouteMatcher());
ArgumentResolverConfigurer args = messageHandler
.getArgumentResolverConfigurer();
args.addCustomResolver(principal);
args.addCustomResolver(context);
return messageHandler;
}
Note that RSocketMessageHandler
is currently created in RSocketMessagingAutoConfiguration
Comment From: bclozel
@rwinch @rstoyanchev about that last point, there is a way to achieve that in Spring Boot, but it feels a bit unnatural to have that as an opinion expressed for the developers, rather than a use case that could be supported by the configuration infrastructure.
Right now we're supporting most of the configuration through RSocketStrategies
but not the argument resolvers. In WebFlux, we have a method for that: WebFluxConfigurer.configureArgumentResolvers(ArgumentResolverConfigurer configurer)
.
Is there something to be done at the Spring Framework level that would help here?
The alternative in Spring Boot would be to gather HandlerMethodArgumentResolver
beans and add them to the RSocketMessageHandler
we're creating. Note that this RSocketMessageHandler
bean is marked as @ConditionalOnMissingBean
so this would not work for developers defining their own.
Comment From: rstoyanchev
It doesn't seem so opinionated to me to have those resolvers applied but on the second point about @ConditoinalOnMissingBean
it would indeed be unfortunate to to lose those defaults. Could this be done as part of the @EnableRSocketSecurity
setup?
Comment From: bclozel
On the Spring Framework side, we're telling developers to create an RSocketMessageHandler
as a bean; this setup is done automatically for Spring Boot users.
I think this could be a two step process:
1. Spring Security should provide those argument resolvers and make it easier for non Spring Boot users to set those up against a custom RSocketMessageHandler
2. Spring Boot could then use this mechanism to auto-configure those resolvers on the RSocketMessageHandler
, maybe via another customizer extension point.
Comment From: rwinch
We can expose the argument resolvers as Beans, but this won't be available until Security 5.2.1 since 5.2.0 is out. Perhaps for that reason, I should create another ticket?
Comment From: rwinch
@rstoyanchev I'd like to add...I second @bclozel question:
Is there something to be done at the Spring Framework level that would help here?
In MVC Framework provides WebMvcConfigurer.addArgumentResolvers
which allows configuring argument resolvers. In WebFlux Framework provides WebFluxConfigurer.configureArgumentResolvers
which allows configuring argument resolvers. For that reason, it is quite surprising that framework doesn't provide something similar for messaging.
Comment From: rstoyanchev
Actually what I had in mind was that Spring Security would use a preProcess hook to find the RSocketMessageHandler
bean and access its argumentResolverConfigurer
property, and insert those resolves.
The argument resolver options in WebMvc and WebFlux go along with the @Enable
annotations, which we don't have for RSocket. I don't think we should keep adding options to RSocketStrategies
which currently has some responder-only options but at least is neutral for annotation vs functional programming models.
Comment From: rstoyanchev
As for adding @Enable
for RSocket, that is an option, although it would probably be config for 1 bean, or 2 at most. It seemed avoidable at the time.
Comment From: mbhave
Thanks for your inputs, everyone. I think this is a separate issue to the one originally described. From the discussion above, it sounds like it's either something that needs to change in Spring Framework or Spring Security. We can create another Spring Boot issue based on what the outcome is.
Comment From: rwinch
Thanks @mbhave I have moved the discussion to https://github.com/spring-projects/spring-framework/issues/23751
Comment From: joshlong
Hi,
Having resolvers would be a very nice addition in Spring Framework. But I only want to use @AuthenticatedPrincipal
to inject the currently authenticated principal into my @MessageMapping
-annotated handlers. It's what I would expect when using Spring MVC or WebFlux. This just works in those frameworks.
```package com.example.demo;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication public class DemoApplication {
@RestController
static class HelloController {
@GetMapping("/hello")
String hello(@AuthenticationPrincipal UserDetails userDetails) {
return "hello, " + userDetails.getUsername() + "!";
}
}
@Bean
MapReactiveUserDetailsService authentication() {
var jlong = User.withDefaultPasswordEncoder().username("jlong").password("pw").roles("USER").build();
return new MapReactiveUserDetailsService(jlong);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
It's also preferable to using `ReactiveSecurityContextHolder.getContext().map(auth -> ...)`.
So, until the more elegant custom resolvers and `@EnableRSocket` machinery lands in Spring Framework, could we please add a bean to the Spring Boot autoconfiguration, like the following:
@Bean RSocketMessageHandler messageHandler(RSocketStrategies strategies) { var rmh = new RSocketMessageHandler(); rmh.getArgumentResolverConfigurer().addCustomResolver(new AuthenticationPrincipalArgumentResolver()); rmh.setRSocketStrategies(strategies); return rmh; } ```
As far as I understand it, nothing would break for the user, and - in addition, this would allow Spring Security to work better? And, if we're worried, I guess users could opt-out (but I would argue it should be enabled by default) with some sort of configuration property?
Comment From: joshlong
Oh. It seems RSocketMessageHandler
is a singleton - when I override it, I override the default one provided by Spring Boot. So, maybe Boot could expose a RSocketMessageHandlerCustomizer
so that everyone gets a bite at the apple?
Comment From: wilkinsona
Hi, @joshlong. Can you please open a new issue for the above so that it doesn’t get lost?
Comment From: joshlong
I just opened https://github.com/spring-projects/spring-boot/issues/20303 - thanks for your time