You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中RabbitMQ MessageListener的SecurityContextHolder返回null问题

解决RabbitMQ MessageListener中SecurityContextHolder获取不到上下文的问题

问题原因

RabbitMQ的消息监听线程属于独立线程池,不会自动继承Web请求线程(或其他业务线程)的SecurityContext,所以onMessage方法里调用SecurityContextHolder.getContext()会返回null;而Web层等其他业务代码运行在Spring Security管理的线程中,能正常获取上下文。

解决方案

方案一:配置SecurityContext自动传播(需发送端配合)

通过配置RabbitMQ监听器容器,让监听线程继承发送端的SecurityContext,需发送端将上下文存入消息头,接收端配置拦截器自动解析:

  1. 接收端配置类:
@Configuration
public class RabbitMQConfig {

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory,
                                                                             SecurityContextPropagationChannelInterceptor securityContextInterceptor) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        // 添加SecurityContext传播拦截器
        factory.setAfterReceivePostProcessors(securityContextInterceptor);
        return factory;
    }

    @Bean
    public SecurityContextPropagationChannelInterceptor securityContextPropagationChannelInterceptor() {
        return new SecurityContextPropagationChannelInterceptor();
    }
}
  1. 发送端发送消息时携带SecurityContext:
// 获取当前上下文
SecurityContext securityContext = SecurityContextHolder.getContext();
// 构造消息头
MessageHeaders headers = new MessageHeaders(Map.of(
    SecurityContextPropagationChannelInterceptor.SECURITY_CONTEXT_HEADER,
    securityContext
));
// 构造消息并发送
Message<Facture> message = MessageBuilder.createMessage(facture, headers);
rabbitTemplate.send("exchange-name", "routing-key", message);

方案二:手动设置固定身份(无需发送端配合)

如果消息处理不需要依赖发送端用户,可在onMessage方法开头手动设置系统用户的认证信息,处理完成后清空上下文避免线程复用问题:

@Override
public void onMessage(Message message) {
    // 构造系统用户认证信息,替换为你的实际用户数据
    ConnectedUser systemUser = new ConnectedUser("system_user", "System", "Admin");
    Authentication auth = new UsernamePasswordAuthenticationToken(
        systemUser,
        null,
        Collections.singletonList(new SimpleGrantedAuthority("ROLE_SYSTEM"))
    );
    // 设置上下文
    SecurityContextHolder.getContext().setAuthentication(auth);

    try {
        Gson gson = new Gson();
        ControleResult resultControle = gson.fromJson(new String(message.getBody()), Facture.class);
        Optional<Facture> optional = dossierDao.findById(resultControle.getId());
        Facture facture= null;
        if (optional.isPresent()) {
            facture= optional.get();
        }
      
        factureDao.save(facture);
        log.info("USER     " + getLoggedInUserName());   
    } finally {
        // 清空上下文,防止线程池复用导致的上下文污染
        SecurityContextHolder.clearContext();
    }
}

方案三:使用@RabbitListener时开启自动传播

如果项目用@RabbitListener注解替代直接实现MessageListener,可直接在配置文件中开启SecurityContext传播:

在application.properties中添加:

# 针对simple类型监听器容器
spring.rabbitmq.listener.simple.transmit-security-context=true
# 针对direct类型监听器容器(如果使用的话)
spring.rabbitmq.listener.direct.transmit-security-context=true

同样需要发送端按照方案一的方式将SecurityContext存入消息头。

内容的提问来源于stack exchange,提问作者MED

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 03:22:16