RSocket Java Spring @AuthenticationPrincipal с JWT

Как мы можем использовать @AuthenticationPrincipal с методом RSocket @AuthenticationPrincipal Mono token

    public Mono<String> uppercase(String s, @AuthenticationPrincipal Mono<JwtAuthenticationToken> token) { 
        //Token is always null 
        return Mono.just(s.toUpperCase());
    }

Я создал класс RSocketSecurityConfiguration:

@Configuration
@EnableRSocketSecurity
@EnableReactiveMethodSecurity
@Slf4j
public class RSocketSecurityConfiguration {

    @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
    private String issuerUri;

    @Bean
    PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
        rsocket
                .authorizePayload(authorize ->
                        authorize
                                .anyRequest().authenticated()
                                .anyExchange().permitAll()
                )
                .jwt(jwtSpec -> {
                    jwtSpec.authenticationManager(jwtReactiveAuthenticationManager(reactiveJwtDecoder()));
                });
        return rsocket.build();
    }
   @Bean
ReactiveJwtDecoder reactiveJwtDecoder() {

    NimbusReactiveJwtDecoder decoder = (NimbusReactiveJwtDecoder)
            ReactiveJwtDecoders.fromOidcIssuerLocation(issuerUri);
    return decoder;
}

   @Bean
    public JwtReactiveAuthenticationManager jwtReactiveAuthenticationManager(ReactiveJwtDecoder reactiveJwtDecoder) {
        JwtReactiveAuthenticationManager jwtReactiveAuthenticationManager = new JwtReactiveAuthenticationManager(reactiveJwtDecoder);

        JwtAuthenticationConverter authenticationConverter = new JwtAuthenticationConverter();
        JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
        authenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        jwtReactiveAuthenticationManager.setJwtAuthenticationConverter( new ReactiveJwtAuthenticationConverterAdapter(authenticationConverter));

        return jwtReactiveAuthenticationManager;
    }
@Bean
RSocketMessageHandler messageHandler(RSocketStrategies strategies) {
    RSocketMessageHandler mh = new RSocketMessageHandler();
    mh.getArgumentResolverConfigurer().addCustomResolver(new AuthenticationPrincipalArgumentResolver());
    mh.setRSocketStrategies(strategies);

    return mh;
}

Полный UpperCaseController:

@Slf4j
@Controller
public class UpperCaseController {

    @MessageMapping("uppercase")
    public Mono<String> uppercase(String s, @AuthenticationPrincipal Mono<JwtAuthenticationToken> token) {
        JwtAuthenticationToken currentToken = token.block();
        if ( currentToken == null ) {
            log.info("token is null");
        }
        return Mono.just(s.toUpperCase());
    }
}

Полный ConnectController:

@Slf4j
@Controller
public class ConnectController {

    @ConnectMapping("connect")
    void connectShellClientAndAskForTelemetry(RSocketRequester requester,
                                              @Payload String client) {

        requester.rsocket()
                .onClose()
                .doFirst(() -> {
                    // Add all new clients to a client list
                    log.info("Client: {} CONNECTED.", client);
                })
                .doOnError(error -> {
                    // Warn when channels are closed by clients
                    log.warn("Channel to client {} CLOSED", client);
                })
                .doFinally(consumer -> {
                    // Remove disconnected clients from the client list

                    log.info("Client {} DISCONNECTED", client);
                })
                .subscribe();
    }
}

Клиент RSocket:

@Component
@Slf4j
public class RSocketClient {

    private static final MimeType SIMPLE_AUTH = MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
    MimeType BEARER_AUTH =
            MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());

    private static final String BEARER_TOKEN = "....";
    private final RSocketRequester requester;
    private RSocketStrategies rsocketStrategies;

    public RSocketClient(RSocketRequester.Builder requesterBuilder,
                         @Qualifier("rSocketStrategies") RSocketStrategies strategies) {
        this.rsocketStrategies = strategies;
        SocketAcceptor responder = RSocketMessageHandler.responder(rsocketStrategies, new RSocketClientHandler());
            requester = requesterBuilder
                .setupRoute("connect")
                .setupData("MyTestClient")
                                .setupMetadata(new BearerTokenMetadata(BEARER_TOKEN), BEARER_AUTH)
                .rsocketStrategies(builder ->
                        builder.encoder(new BearerTokenAuthenticationEncoder()))
                .rsocketConnector(connector -> connector.acceptor(responder))
                .connectTcp("localhost", 7000)
                .block();

        requester.rsocket()
                .onClose()
                .doOnError(error -> log.warn("Connection CLOSED"))
                .doFinally(consumer -> log.info("Client DISCONNECTED"))
                .subscribe();
    }
    public void uppercase() {
               String response = requester
                .route("uppercase")
                .metadata(BEARER_TOKEN, BEARER_AUTH)
                .data("Hello")
                .retrieveMono(String.class).block();
        log.info(response);
    }
}

Я сделал что-то очень похожее для Spring REST, и он отлично работает, но для RSocket токен всегда равен нулю.


person dk-it    schedule 20.01.2021    source источник


Ответы (1)


Я предполагаю, что вы начали с https://spring.io/blog/2020/06/17/getting-started-with-rsocket-spring-security

Мне удалось заставить это работать для моей кодовой базы, используя другой тип, чем @Payload

  @ConnectMapping
  fun handle(requester: RSocketRequester, @AuthenticationPrincipal jwt: String) {
    logger.debug("connected $jwt")
  }
  @MessageMapping("runCommand")
  suspend fun runCommand(request: CommandRequest, rSocketRequester: RSocketRequester, @AuthenticationPrincipal jwt: String): Flow<CommandResponse> {
...
  }
person Yuri Schimke    schedule 20.01.2021
comment
Можете ли вы поделиться своей конфигурацией безопасности? Я изменил токен \ @AuthenticationPrincipal Mono ‹JwtAuthenticationToken› на токен \ @AuthenticationPrincipal Mono ‹String›, а токен \ @AuthenticationPrincipal String не работал. Я также добавил в ConnectMapping, что тоже не сработало. - person dk-it; 22.01.2021
comment