如何在 Spring Boot 中将当前的 SecurityContext 传播到我的 @RabbitListener?

问题描述 投票:0回答:1

用例:我想要一个 REST API,它将使用 RabbitMQ/AMQP 在系统中触发一个事件,并且我希望将当前登录用户的 SecurityContext 传播到该事件,以便在中使用审核注释我的 JPA 存储库 (lastModifiedBy) 和用于检查服务中用户角色的注释。

我目前在声明 AmqpTemplate bean 时有这段代码:

rabbitTemplate.setBeforePublishPostProcessors(
  MessagePostProcessor { message ->
    val authentication = SecurityContextHolder.getContext().authentication
    message.messageProperties.setHeader("x-user-id", objectMapper.writeValueAsString(authentication))
    message
  }
)

我想序列化 Authentication 对象,然后在消费者端反序列化它。

我目前的情况是这样的:

@RabbitListener(queues = [TrackingEventPublisher.QUEUE_NAME])
fun handleTrackingMessage(trackingEvent: TrackingEvent, headers: MessageHeaders) {
  val xUserId = headers["x-user-id"].toString()
  val propagatedAuthentication = try {
    objectMapper.readValue(xUserId, AnonymousAuthenticationToken::class.java)
  } catch (_: Exception) {
    objectMapper.readValue(xUserId, OAuth2AuthenticationToken::class.java)
  }
  SecurityContextHolder.setContext(
    SecurityContextHolder.createEmptyContext().apply {
      authentication = propagatedAuthentication
    }
  )
  logger.info { "Logging event: ${trackingEvent.uuid} started, headers: ${headers}" }
  logger.info { "Logging event: security ${SecurityContextHolder.getContext().authentication}" }
  trackingService.save(trackingEvent)
  logger.info { "Logging event: ${trackingEvent.uuid} finished" }
}

这种方法的问题是 ObjectMapper 无法实际反序列化 Authentication 对象。它不能这样做,因为该对象的字段是抽象类或接口,并且它们无法实例化(它们也没有默认构造函数,而 Jackson 需要它)。

我想知道是否有一些惯用的方法可以做到这一点,或者也许某些图书馆的人已经做过了。

我知道有一种利用 Spring Integration 的不同方法,它确实支持 SecurityContext 传播,但我还没有准备好学习 EIP,所以我首先想做一些更简单、更接近 AMQP 协议的事情,并且与协议无关。

您对如何实际解决这个问题有任何想法吗?

spring spring-security spring-amqp
1个回答
0
投票

我认为尝试序列化

Authentication
不是正确的方法。我建议您研究存储
Principal
或凭证而不是这些 AMQP 标头。然后在消费者端验证收到的委托人等等。

© www.soinside.com 2019 - 2024. All rights reserved.