Spring Boot中RSocket客户端如何通过元数据正确传递JWT令牌
问题原因
令牌开头的异常字符不是传输过程追加的脏数据,是RSocket Bearer认证元数据编码规范自带的类型标识字节:AuthMetadataCodec.encodeBearerMetadata方法返回的ByteBuf第1位会固定写入Bearer认证对应的类型标记(固定值0x80),如果服务端直接把整段认证元数据的字节数组转成字符串读取,就会把这个标识位识别为乱码字符。
另外你当前的客户端写法存在内存泄漏隐患:encodeBearerMetadata返回的ByteBuf在加入复合元数据后没有手动释放,高并发场景下会触发Netty内存泄漏告警。
正确实现方案
客户端JWT元数据传递(Java + Spring Boot)
推荐优先使用Spring RSocket内置的拦截器统一注入JWT,不用手动操作ByteBuf,从根源避免编码错误:
@Configuration public class RSocketClientConfig { @Bean public RSocketRequester rSocketRequester(RSocketStrategies strategies) { RSocketRequester.Builder builder = RSocketRequester.builder() .rsocketConnector(connector -> connector.interceptors(interceptorRegistry -> interceptorRegistry.forRequester((requesterMono) -> requesterMono .map(requester -> (RSocket) new RSocketProxy(requester) { @Override public Mono<Payload> requestResponse(Payload payload) { String jwt = TenantContext.getCurrentToken(); CompositeByteBuf metadata = (CompositeByteBuf) payload.metadata(); metadata.addComponent(true, BearerTokenMetadata.encode(ByteBufAllocator.DEFAULT, jwt).getMetadata()); return super.requestResponse(payload); } }) )); return builder.tcp("服务端地址", 服务端端口, strategies); } }
如果需要手动组装复合元数据,参考以下写法,注意临时缓冲区的释放:
CompositeByteBuf compositeMetadata = ByteBufAllocator.DEFAULT.compositeBuffer(); // 写入路由元数据 ByteBuf routingMetadata = TaggingMetadataCodec.createRoutingMetadata( ByteBufAllocator.DEFAULT, Collections.singletonList("目标接口路由") ).getContent(); CompositeMetadataCodec.encodeAndAddMetadata(compositeMetadata, ByteBufAllocator.DEFAULT, RoutingMetadata.ROUTING_METADATA_TYPE, routingMetadata); routingMetadata.release(); // 写入Bearer认证元数据 ByteBuf authMetadata = AuthMetadataCodec.encodeBearerMetadata( ByteBufAllocator.DEFAULT, TenantContext.getCurrentToken().toCharArray() ); CompositeMetadataCodec.encodeAndAddMetadata(compositeMetadata, ByteBufAllocator.DEFAULT, BearerTokenMetadata.BEARER_AUTHENTICATION_MIME_TYPE, authMetadata); authMetadata.release();
注意不要硬编码元数据MIME类型字符串,直接引用BearerTokenMetadata.BEARER_AUTHENTICATION_MIME_TYPE常量,避免版本迭代时类型值不匹配导致解析失败。
服务端JWT解析
不要直接将认证元数据的字节数组转字符串,使用官方解码器读取,会自动跳过开头的类型标识字节:
@MessageMapping("目标接口路由") public Mono<ResponseVO> handleRequest(Payload payload) { // 解码认证元数据,自动过滤类型标识字节,拿到纯净的JWT BearerTokenMetadata tokenMetadata = BearerTokenMetadata.decode(payload.metadata()); String jwt = tokenMetadata.getToken(); // 后续执行JWT校验、业务处理逻辑即可 return Mono.just(new ResponseVO()); }
如果已经集成Spring Security做RSocket认证,直接配置JWT认证规则即可,不需要手动解析元数据:
@Configuration @EnableRSocketSecurity public class RSocketSecurityConfig { @Bean public PayloadSocketAcceptorInterceptor rsocketAuthInterceptor(RSocketSecurity security) { security .authorizePayload(authz -> authz .anyRequest().authenticated() ) .jwt(Customizer.withDefaults()); return security.build(); } }
避坑要点
- 禁止直接将
AuthMetadataCodec.encodeBearerMetadata返回的ByteBuf转字符串作为令牌使用,必须通过配套的decode方法解析 - 手动操作Netty ByteBuf时严格遵守引用计数规则,临时缓冲区使用完成后及时调用
release()释放 - 不要硬编码RSocket扩展协议的MIME类型、标识位数值,优先使用官方提供的常量,避免跨版本兼容问题
内容的提问来源于stack exchange,提问作者Shivangi Varshney
相关产品推荐
相关产品推荐

