【问题标题】:Issue with Connecting IBM MQ Using Externalized User Credential - Spring Boot + JMS + IBM MQ 8.0.0.9使用外部化用户凭证连接 IBM MQ 的问题 - Spring Boot + JMS + IBM MQ 8.0.0.9
【发布时间】:2019-06-16 04:00:27
【问题描述】:

我需要有关标题主题的帮助。在这里请求您的建议。

我正在使用 Spring Boot 框架从 IBM MQ 8.0.0.9 获取消息。所附程序符合基本预期。但是,它始终使用操作系统凭证(特别是 Windows 登录凭证)连接到 IBM MQ。但我希望它使用属性文件中给出的凭证。即使我故意在属性文件中提供了错误的凭证,程序也很高兴地连接到 IBM MQ(使用 Windows 登录凭证,它也可以访问 IBM MQ)。

这是 MQ 详细信息 - MQ 版本 8.0.0.9, 质量管理版本 8.0.0.9, 康诺 - 未设置, CHLAUTH - 残疾人, QMGR CONNAUTH - 未设置, CHCKCLNT - 可选, AUTHINFO - SYS.DEFAULT.AUTHINFO.IDPWOS

您能否建议我在附加程序中做错了什么。我也尝试过注释的代码行。

@EnableJms
@Configuration
@EnableTransactionManagement
public class JmsConfig {
    @Value("${ibm.mq.host}")
    private String host;
    @Value("${ibm.mq.port}")
    private Integer port;
    @Value("${ibm.mq.queueManager}")
    private String queueManager;
    @Value("${ibm.mq.channel}")
    private String channel;
    @Value("${ibm.mq.responseQueue}")
    private String responseQueue;
    @Value("${ibm.mq.userName}")
    private String userName;
    @Value("${ibm.mq.password}")
    private String password;
    @Value("${ibm.mq.receiveTimeout}")
    private long timeout;

    @Autowired(required=true)
    @Qualifier(value="responseListener")
    MessageListener  responseListener;

    @Bean (name="queueConnectionFactory")
    public MQQueueConnectionFactory mqQueueConnectionFactory() {
        MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();

        try {
            mqQueueConnectionFactory.setHostName(host);
            mqQueueConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
            mqQueueConnectionFactory.setChannel(channel);
            mqQueueConnectionFactory.setPort(port);
            mqQueueConnectionFactory.setQueueManager(queueManager);
            mqQueueConnectionFactory.setCCSID(819);
            /*
            mqQueueConnectionFactory.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, true);
            mqQueueConnectionFactory.setStringProperty(WMQConstants.USERID, userName);
            mqQueueConnectionFactory.setStringProperty(WMQConstants.PASSWORD, password);
            mqQueueConnectionFactory.setStringProperty(CMQC.USER_ID_PROPERTY, userName);
            mqQueueConnectionFactory.setStringProperty(CMQC.PASSWORD_PROPERTY, password);
            */
        } catch (JMSException e) {
            log.error("Error occured: " + e);
        }

        return mqQueueConnectionFactory;
    }

    @Primary
    @Bean (name="userCredentialsConnectionFactoryAdapter")
    @DependsOn(value = { "queueConnectionFactory" })
    UserCredentialsConnectionFactoryAdapter getUserCredentialsConnectionFactoryAdapter(
            MQQueueConnectionFactory mqQueueConnectionFactory) {
        UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter = new UserCredentialsConnectionFactoryAdapter();
        userCredentialsConnectionFactoryAdapter.setUsername(userName);
        userCredentialsConnectionFactoryAdapter.setPassword(password);
        userCredentialsConnectionFactoryAdapter.setTargetConnectionFactory(mqQueueConnectionFactory);
        return userCredentialsConnectionFactoryAdapter;
    }

    @Bean (name="simpleMessageListenerContainer")
    @DependsOn(value = { "userCredentialsConnectionFactoryAdapter" })
    public SimpleMessageListenerContainer queueResponseContainer(ConnectionFactory userCredentialsConnectionFactoryAdapter) {
        SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer();
        simpleMessageListenerContainer.setConnectionFactory(userCredentialsConnectionFactoryAdapter);
        simpleMessageListenerContainer.setConnectLazily(true);
        simpleMessageListenerContainer.setDestinationName(responseQueue);
        simpleMessageListenerContainer.setMessageListener(responseListener);
        return simpleMessageListenerContainer;
    }

    @Bean
    public JmsOperations jmsOperations(ConnectionFactory userCredentialsConnectionFactoryAdapter) {
        JmsTemplate jmsTemplate = new JmsTemplate(userCredentialsConnectionFactoryAdapter);
        jmsTemplate.setReceiveTimeout(timeout);
        return jmsTemplate;
    }
}

====================

@Component(value="responseListener")
public class ResponseListener implements MessageListener {

    public void onMessage(Message message) {    
        ...
    }
}

===================

@Component
public class ContainerChecker {

    @Autowired
    SimpleMessageListenerContainer  queueContainer;

    @Scheduled(fixedRate = 300000)
    public void reportContainerStatus() throws ServiceException{
        if(!queueContainer.isActive()) {
            ...
        } else {
            ...
        }
    }
}

【问题讨论】:

  • 您还管理队列管理器吗?队列管理器上使用的是什么 MQ 版本?您是否启用了CONNAUTH?如果 MQ 队列管理器是 v7.5 或更低版本(如果没有扩展支持,它将失去支持),开箱即用的 MQ 不知道如何验证用户,并且基于 CHLAUTH 之类的设置可能只接受断言的用户基于您的 MQ v8 客户端应用程序将是您的程序在其下运行的用户。对于 MQ v8 也是如此,其中 QMGR CONNAUTH 为空白,或者如果在 AUTHINFO 对象中填充了 CHCKCLNT(NONE) 集。
  • 如果您的队列管理器配置符合上述情况之一,那么从应用程序端您可以将WMQConstants.USER_AUTHENTICATION_MQCSP 设置为FALSE 或不设置默认为FALSE,这将导致IBM MQ Classes for JMS 将您在 WMQConstants.USERID 中指定的用户发送为断言用户,而不是您的程序在其下运行的用户。请记住,不会进行身份验证,队列管理器将忽略密码,任何知道要发送的主机/端口/通道/用户的人都可以连接。
  • 如果您可以根据上述问题提供更多详细信息,那么我可以在答案中给出更具体的建议。
  • @JoshMc 首先谢谢你。我不管理 MQ,所以我需要先得到这些答案。我会回复你(即使它得到解决)。 BTW MQ 版本是 8.x
  • 队列管理器是否也在 8.0?

标签: spring-boot jms ibm-mq spring-jms


【解决方案1】:

根据您的 cmets 中的更新:

  • 您的应用程序正在使用 IBM MQ v8.0.0.9 中的用于 JMS 的 IBM MQ 类
  • 您的应用程序正在连接到同样运行 v8.0.0.9 的 IBM MQ 队列管理器。
    • 队列管理器已禁用 CONNAUTH [QMGR CONNAUTH('')]
    • 队列管理器禁用了 CHLAUTH [`QMGR CHLAUTH(DISABLED)']。

在您的配置中,您显示以下 bean:

@Bean (name="userCredentialsConnectionFactoryAdapter")
@DependsOn(value = { "queueConnectionFactory" })
UserCredentialsConnectionFactoryAdapter getUserCredentialsConnectionFactoryAdapter(
        MQQueueConnectionFactory mqQueueConnectionFactory) {
    UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter = new UserCredentialsConnectionFactoryAdapter();
    userCredentialsConnectionFactoryAdapter.setUsername(userName);
    userCredentialsConnectionFactoryAdapter.setPassword(password);
    userCredentialsConnectionFactoryAdapter.setTargetConnectionFactory(mqQueueConnectionFactory);
    return userCredentialsConnectionFactoryAdapter;
}

您还有以下注释掉的代码:

        /*
        mqQueueConnectionFactory.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, true);
        mqQueueConnectionFactory.setStringProperty(WMQConstants.USERID, userName);
        mqQueueConnectionFactory.setStringProperty(WMQConstants.PASSWORD, password);
        mqQueueConnectionFactory.setStringProperty(CMQC.USER_ID_PROPERTY, userName);
        mqQueueConnectionFactory.setStringProperty(CMQC.PASSWORD_PROPERTY, password);
        */

对于 JMS v8.0 及更高版本的 IBM MQ 类,设置 WMQConstants.USER_AUTHENTICATION_MQCSP 的默认值为 FALSE。这表示客户端应该在兼容模式下工作,这意味着 MQ 客户端像在 7.5 及更低版本中一样工作,MQCSP 在那些旧版本中不存在,并且指定的用户名和密码在一些名为 RemoteUserIdentifier 的字段中发送,并且RemotePassword 并且限制为 12 个字符。

对于 MQ v8 和更高版本,用于 JMS 的 IBM MQ 类现在支持一种新的方式来发送用户名和密码,这就是 MQCSP 结构。这有一些好处,例如它可以输入超过 12 个字符的密码。如果WMQConstants.USER_AUTHENTICATION_MQCSPTRUE,则指定的用户名和密码将在MQCSP 结构中发送,而在兼容模式下使用的RemoteUserIdentifier 将填入进程正在运行的用户名,RemotePassword 字段为留空。

我找不到任何东西让我相信 userCredentialsConnectionFactoryAdapter bean 对 WMQConstants.USER_AUTHENTICATION_MQCSP 设置做了任何事情,所以这会让我相信它应该发送您在名为 @ 的字段中指定的用户名和密码987654334@ 和RemotePassword。如果您使用注释掉的代码,它将发送您在 MQCSP 中指定的用户名和密码,并且您正在运行该进程的 Windows 登录 ID 将在 RemoteUserIdentifier 字段中发送。


具有您指定配置的 v8 队列管理器将完全忽略 MQCSP 结构中的任何内容,并且只会查看 RemoteUserIdentifier 字段。您还说 CHLAUTH 已禁用,因此这意味着没有 CHLAUTH 规则可以限制或更改客户端发送的用户名。如果SVRCONN 频道的MCAUSER 为空白,则RemoteUserIdentifier 字段中发送的用户ID 将用于队列授权检查。

我怀疑当您注意到队列管理器出现您的“Windows 登录凭据”时,您将 WMQConstants.USER_AUTHENTICATION_MQCSP 设置为 TRUE。通过将其设置为 FALSE,您可以指定要发送到 MQ 的任何 ID,并且由于队列管理器的配置,它将接受该 ID 并使用它,因此不会验证密码。

由于 MQ 不检查密码,因此无论您为密码指定什么值都没有关系,以下将能够连接发送指定的用户名。

mqQueueConnectionFactory.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, FALSE);
mqQueueConnectionFactory.setStringProperty(WMQConstants.USERID, userName); 

关于队列管理器设置的注意事项

由于未配置 CONNAUTH 并且 CHLAUTH 也已禁用,您可以连接到队列管理器并指定 mqm id(如果队列管理器在 Unix 上)或 MUSR_MQADMIN id(如果队列管理器在 Windows 上) 并且您将对队列管理器上的所有队列拥有完整的 MQ 权限。

当前配置没有提供任何安全措施来防止任何有权通过网络连接到队列管理器的主机和端口的人访问队列管理器可用的任何资源,在大多数情况下,这也可以用来执行您想要的任何操作运行队列管理器的服务器。

【讨论】:

  • 谢谢。 bean“userCredentialsConnectionFactoryAdapter”已被取消,相关依赖关系已通过“queueConnectionFactory”解决。此外,我取消了注释的代码行(从“true”更改为“false”)。但是,最后 2 行可能是多余的。
猜你喜欢
  • 2023-03-05
  • 2021-09-25
  • 2020-09-29
  • 1970-01-01
  • 1970-01-01
  • 2021-08-04
  • 1970-01-01
  • 2019-12-31
  • 2020-04-23
相关资源
最近更新 更多