用例:我想要一个 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 协议的事情,并且与协议无关。
您对如何实际解决这个问题有任何想法吗?
我认为尝试序列化
Authentication
不是正确的方法。我建议您研究存储 Principal
或凭证而不是这些 AMQP 标头。然后在消费者端验证收到的委托人等等。