使用外部化用户凭证连接 IBM MQ 的问题 - Spring Boot + JMS + IBM MQ 8.0.0.9

Issue with Connecting IBM MQ Using Externalized User Credential - Spring Boot + JMS + IBM MQ 8.0.0.9

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

我正在使用 spring 引导框架从 IBM MQ 8.0.0.9 获取消息。附加程序满足基本期望。但是,它始终使用 OS 凭据(特别是 Windows 登录凭据)连接到 IBM MQ。但我希望它使用 属性 文件中给出的凭据。即使我故意在 属性 文件中提供错误的凭据,该程序也可以愉快地与 IBM MQ 连接(使用也可以访问 IBM MQ 的 windows 登录凭据)。

这是 MQ 详细信息 - MQ 版本 8.0.0.9, 质量管理版本 8.0.0.9, CONNAUTH - 未设置, CHLAUTH - 残疾人, QMGR CONNAUTH - 未设置, CHCKCLNT - 可选的, 授权信息 - 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 {
            ...
        }
    }
}

根据您评论中的更新:

  • 您的应用程序正在使用 IBM MQ 类 for JMS from IBM MQ v8.0.0.9
  • 您的应用程序也连接到 IBM MQ 队列管理器 运行 v8.0.0.9。
    • 队列管理器已禁用 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_MQCSPFALSE。这表示客户端应该在兼容模式下工作,这意味着 MQ 客户端像在 7.5 及更低版本中一样工作,MQCSP 在那些旧版本中不存在,并且指定的用户名和密码在某些名为 [=16= 的字段中发送] 和 RemotePassword 并被限制为 12 个字符。

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

我找不到任何让我相信 userCredentialsConnectionFactoryAdapter bean 对 WMQConstants.USER_AUTHENTICATION_MQCSP 设置有任何作用的东西,所以这会让我相信它应该向您发送用户名和密码在名为 RemoteUserIdentifierRemotePassword 的字段中指定。如果您使用注释掉的代码,它将发送您在 MQCSP 中指定的用户名和密码以及您的 windows 登录 ID,即 运行,该过程将在 RemoteUserIdentifier 字段中发送。


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

我怀疑当您注意到您的 "windows login credential" 传到队列管理器时,是在您将 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 权限。

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