OAuth2 authorization with Spring Security and Rabbitmq

£可爱£侵袭症+ 提交于 2019-12-05 02:16:51

问题


We currently have a number of Spring microservices that are communicating with REST endpoints and RabbitMQ queues. We have just implemented OAuth2 security on all of the services, and the REST endpoints are appropriately secured.

We have a library that we wrote which creates the RabbitTemplate and AmqpAdmin beans so that the boilerplate code doesn't have to be done in every service. We are connecting to the RabbitMQ server in Spring with a specific user for regular clients, and another for admins. We don't want to connect to the RabbitMQ server as the individual user.

Is it possible, if we pass the access token in the rabbit message header, to configure the RabbitTemplate to check the token before the message gets handled? Is this something that can/should be done in the AfterReceive/BeforePublish processors globally for the template? Or will this need to be checked individually in each listener method?

Thanks


回答1:


I was able to work out a solution by creating a custom MessageListenerContainerFactory and MessageListenerContainer.

CustomMessageListenerContainerFactory.java:


public class CustomMessageListenerContainerFactory extends AbstractRabbitListenerContainerFactory {

    DefaultTokenServices tokenServices;

    public CustomMessageListenerContainerFactory(DefaultTokenServices tokenServices) {
        this.tokenServices = tokenServices;
    }

    /**
     * Create an empty container instance.
     *
     * @return the new container instance.
     */
    @Override
    protected CustomMessageListenerContainer createContainerInstance() {
        return new CustomMessageListenerContainer(tokenServices);
    }
}

CustomMessageListenerContainer.java:


public class CustomMessageListenerContainer extends SimpleMessageListenerContainer {
    private final static String errorMessage = "No valid credentials found in request: {}";
    private final static String handlingMessage = "Handling queue: {}";
    private final static String receivedMessage = "Received Message: {}";
    private final static Logger logger = LoggerFactory.getLogger(CustomMessageListenerContainer.class);


    private DefaultTokenServices tokenServices;

    /**
     * Constructor
     *
     * @param tokenServices The instance of DefaultTokenServices used to decrypt the access token.
     */
    public CustomMessageListenerContainer(DefaultTokenServices tokenServices) {
        this.tokenServices = tokenServices;
    }

    /**
     * This method checks to see if there is a valid authorization
     *
     * @param channel   The AMQP channel on which the message was published.
     * @param messageIn The incoming message.
     * @throws Exception Throws an exception when there are no valid credentials in the message.
     */
    @Override
    protected void executeListener(Channel channel, Message messageIn) throws Exception {
        logger.info(handlingMessage, (Object[]) getQueueNames());
        logger.info(receivedMessage, BeanUtils.beanProperties(messageIn));
        if (messageIn.getMessageProperties().getHeaders().keySet().stream().anyMatch(t -> Objects.equals(t.toLowerCase(), "authorization"))) {
            String accessKey = messageIn.getMessageProperties()
                    .getHeaders()
                    .keySet()
                    .stream()
                    .filter(t -> Objects.equals(t.toLowerCase(), "authorization"))
                    .findFirst()
                    .get();
            OAuth2Authentication auth = tokenServices.loadAuthentication(messageIn.getMessageProperties().getHeaders().get(accessKey).toString());
            // If the token is expired, there will be no auth.
            if (auth != null) {
                SecurityContextHolder.getContext().setAuthentication(auth);
                super.executeListener(channel, messageIn);
                return;
            }
        }
        rejectMessage(channel, messageIn);
    }

    private void rejectMessage(Channel channel, Message messageIn) throws Exception {
        logger.info(errorMessage, (Object[]) getQueueNames());
        String localMessage = errorMessage.replace("{}", String.join(", ", getQueueNames()));
        if (messageIn.getMessageProperties().getReplyTo() != null) {
            channel.basicPublish("",
                    messageIn.getMessageProperties().getReplyTo(),
                    new AMQP.BasicProperties.Builder()
                            .contentType("application/json")
                            .correlationId(messageIn.getMessageProperties().getCorrelationId())
                            .build(),
                    "{\"errorMessage\":\"".concat(localMessage).concat("\"}").getBytes());
        }
        throw new AmqpRejectAndDontRequeueException(localMessage);
    }
}

CustomRabbitListenerConfigurer.java:

...
@Override
    public void configureRabbitListeners(final RabbitListenerEndpointRegistrar registrar) {
        registrar.setMessageHandlerMethodFactory(messageHandlerMethodFactory());
        CustomMessageListenerContainerfactory factory = new CustomMessageListenerContainerfactory(tokenServices);
        ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class);
        factory.setConnectionFactory(connectionFactory);
        registrar.setContainerFactory(factory);
    }
...



来源:https://stackoverflow.com/questions/50358936/oauth2-authorization-with-spring-security-and-rabbitmq

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!