spring-security Spring RSocket安全性JWT访问被拒绝错误

rqenqsqc  于 2022-11-11  发布在  Spring
关注(0)|答案(1)|浏览(156)

I am trying to use Spring Boot RSocket with Security using JWT Tokens. It is giving me an Access Denied error with no other useful information to help debug with?
Access Denied.
ApplicationErrorException (0x201): Access Denied at app//io.rsocket.exceptions.Exceptions.from(Exceptions.java:76) at app//io.rsocket.core.RSocketRequester.handleFrame(RSocketRequester.java:261) at app//io.rsocket.core.RSocketRequester.handleIncomingFrames(RSocketRequester.java:211) at app//reactor.core.publisher.LambdaSubscriber.onNext(LambdaSubscriber.java:160) at app//io.rsocket.core.ClientServerInputMultiplexer$InternalDuplexConnection.onNext(ClientServerInputMultiplexer.java:248) at app//io.rsocket.core.ClientServerInputMultiplexer.onNext(ClientServerInputMultiplexer.java:129) at app//io.rsocket.core.ClientServerInputMultiplexer.onNext(ClientServerInputMultiplexer.java:48) at app//reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) at app//reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:364) at app//reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:404) at app//reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:725) at app//reactor.netty.http.client.WebsocketClientOperations.onInboundNext(WebsocketClientOperations.java:161) at app//reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:93) at app//io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at app//io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at app//io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) at app//io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:327) at app//io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:299) at app//io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at app//io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at app//io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) at app//io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) at app//io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at app//io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at app//io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) at app//io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) at app//io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:722) at app//io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:658) at app//io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:584) at app//io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496) at app//io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) at app//io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at app//io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base@11.0.14/java.lang.Thread.run(Thread.java:834)

Security Configfile

@Configuration
@EnableRSocketSecurity
@EnableReactiveMethodSecurity
class SecurityConfig {

    @Bean
    fun authorization(rsocketSecurity: RSocketSecurity): PayloadSocketAcceptorInterceptor {
        val security: RSocketSecurity =
            rsocketSecurity.authorizePayload { authorize: RSocketSecurity.AuthorizePayloadsSpec ->
                authorize
                    .anyRequest().authenticated()
                    .anyExchange().permitAll()
            }
                .jwt { jwtSpec ->
                    jwtSpec.authenticationManager(jwtReactiveAuthenticationManager(jwtDecoder()))
                }
        return security.build()
    }

    @Bean
    fun jwtDecoder(): ReactiveJwtDecoder {
        return TokenUtils.jwtAccessTokenDecoder()
    }

    @Bean
    fun jwtReactiveAuthenticationManager(decoder: ReactiveJwtDecoder): JwtReactiveAuthenticationManager {
        val converter = JwtAuthenticationConverter()
        val authoritiesConverter = JwtGrantedAuthoritiesConverter()
        authoritiesConverter.setAuthorityPrefix("ROLE_")
        converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter)
        val manager = JwtReactiveAuthenticationManager(decoder)
        manager.setJwtAuthenticationConverter(ReactiveJwtAuthenticationConverterAdapter(converter))
        return manager
    }

    @Bean
    fun rsocketMessageHandler() = RSocketMessageHandler() .apply {
        argumentResolverConfigurer.addCustomResolver(AuthenticationPrincipalArgumentResolver())
        routeMatcher = PathPatternRouteMatcher()
        rSocketStrategies = rsocketStrategies()
    }

    @Bean
    fun rsocketStrategies() = RSocketStrategies.builder()
        .routeMatcher(PathPatternRouteMatcher())
        .build()
}

Message Controllerfile

@MessageMapping("api.v1.messages")
@Controller
class MessageController {

    @MessageMapping("stream")
    suspend fun receive(
        @Payload inboundMessages: Flow<String>,
        @AuthenticationPrincipal jwt: String
    ) {
        println("MessageController: jwt: $jwt")
        println("MessageController: inbound message: " + inboundMessages.first())
    }
}

Testing usingMessageControllerTestfile

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MessageControllerTest(
    @Autowired val rsocketBuilder: RSocketRequester.Builder,
    @LocalServerPort val serverPort: Int
) {

    @ExperimentalTime
    @ExperimentalCoroutinesApi
    @Test
    fun `test that messages API streams latest messages`() {
        val admin = HelloUser(userId = "9527", password = "password", role = HelloRole.ADMIN)

        val token: UserToken = TokenUtils.generateAccessToken(admin)!!

        val authenticationMimeType: MimeType =
            MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)

        runBlocking {
            val rSocketRequester = rsocketBuilder.websocket(URI("ws://localhost:${serverPort}/rsocket"))

            launch {

                rSocketRequester.route("api.v1.messages.stream")
                    .metadata(token.token!!, authenticationMimeType)
                    .dataWithType(flow {
                        emit(
                            "Hey from test class"
                        )
                    })
                    .retrieveFlow<Void>()
                    .collect()
            }
        }
    }
}

I've add the rest of the code example I did to GitHub https://github.com/CJMobileApps/rsocket-jwt-security-example

8nuwlpux

8nuwlpux1#

我想通了。RSocket目前有一个bug或者只是文档不好。JWTs BearerTokenMetadata.BEARER_AUTHENTICATION_MIME_TYPE的MimeType说它被弃用了,要用MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)代替,但是这不起作用。
当传递标记时,继续使用/将BearerTokenMetadata.BEARER_AUTHENTICATION_MIME_TYPE与标记字符串沿着使用。

.metadata(token.token!!, BearerTokenMetadata.BEARER_AUTHENTICATION_MIME_TYPE)

相关问题