java - org.springframework.jms.JmsSecurityException: 拒绝访问资源: type= <jms>

  显示原文与译文双语对照的内容

当发布到安全的web logic JMS队列时,我收到一个 JmsSecurityException,该队列使用 spring的JmsTemplate 。 麻烦的是,我能够使用等价的非spring代码发布成功地发布。

下面是引发( 。假定目标为 QUEUE.IN )的异常。


org.springframework.jms.JmsSecurityException: Access denied to resource: type=<jms>, application=DistQueue-Module, destinationType=queue, resource=QUEUE.IN, action=send; nested exception is weblogic.jms.common.JMSSecurityException: Access denied to resource: type=<jms>, application=DistQueue-Module, destinationType=queue, resource=QUEUE.IN, action=send
 at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:291)
 at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:168)
 at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:469)
 at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:534)
 at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:526)
 at com.company.jms.publisher.service.impl.JmsServiceImpl.sendMessage(JmsServiceImpl.java:34)
 at com.company.jms.publisher.service.impl.JmsServiceImpl.publish(JmsServiceImpl.java:29)
 at com.company.jms.publisher.controller.MainController.postIt(MainController.java:74)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:606)
 at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
 at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
 at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
 at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
 at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
 at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
 at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
 at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
 at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
 at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
 at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
 at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
 at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:401)
 at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
 at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
 at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:766)
 at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:450)
 at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
 at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
 at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
 at org.mortbay.jetty.Server.handle(Server.java:326)
 at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
 at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:945)
 at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
 at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
 at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
 at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
 at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
2013-11-07 19:26:56.506:WARN::Nested in org.springframework.jms.JmsSecurityException: Access denied to resource: type=<jms>, application=DistQueue-Module, destinationType=queue, resource=QUEUE.IN, action=send; nested exception is weblogic.jms.common.JMSSecurityException: Access denied to resource: type=<jms>, application=DistQueue-Module, destinationType=queue, resource=QUEUE.IN, action=send:
weblogic.jms.common.JMSSecurityException: Access denied to resource: type=<jms>, application=DistQueue-Module, destinationType=queue, resource=QUEUE.IN, action=send
 at weblogic.jms.common.JMSSecurityHelper.checkPermission(JMSSecurityHelper.java:162)
 at weblogic.jms.backend.BEDestinationSecurityImpl.checkSendPermission(BEDestinationSecurityImpl.java:74)
 at weblogic.jms.backend.BEDestinationImpl.checkPermission(BEDestinationImpl.java:2513)
 at weblogic.jms.backend.BEDestinationImpl.sendInitialize(BEDestinationImpl.java:1565)
 at weblogic.jms.backend.BEDestinationImpl.send(BEDestinationImpl.java:2079)
 at weblogic.jms.backend.BEDestinationImpl.wrappedSend(BEDestinationImpl.java:2051)
 at weblogic.jms.backend.BEDestinationImpl.invoke(BEDestinationImpl.java:1539)
 at weblogic.messaging.dispatcher.Request.wrappedFiniteStateMachine(Request.java:961)
 at weblogic.messaging.dispatcher.DispatcherImpl.dispatchAsyncInternal(DispatcherImpl.java:140)
 at weblogic.messaging.dispatcher.DispatcherImpl.dispatchAsync(DispatcherImpl.java:116)
 at weblogic.messaging.dispatcher.Request.dispatchAsync(Request.java:1304)
 at weblogic.jms.dispatcher.Request.dispatchAsync(Request.java:97)
 at weblogic.jms.frontend.FEProducer.doDispatch(FEProducer.java:900)
 at weblogic.jms.frontend.FEProducer.sendRetryDestination(FEProducer.java:1033)
 at weblogic.jms.frontend.FEProducer.send(FEProducer.java:1435)
 at weblogic.jms.frontend.FEProducer.invoke(FEProducer.java:1496)
 at weblogic.messaging.dispatcher.Request.wrappedFiniteStateMachine(Request.java:961)
 at weblogic.messaging.dispatcher.DispatcherServerRef.invoke(DispatcherServerRef.java:276)
 at weblogic.messaging.dispatcher.DispatcherServerRef.handleRequest(DispatcherServerRef.java:141)
 at weblogic.messaging.dispatcher.DispatcherServerRef.access$000(DispatcherServerRef.java:34)
 at weblogic.messaging.dispatcher.DispatcherServerRef$2.run(DispatcherServerRef.java:112)
 at weblogic.work.ExecuteThread.execute(ExecuteThread.java:209)
 at weblogic.work.ExecuteThread.run(ExecuteThread.java:178)

忽略了原始调试的所有池代码,我使用以下配置来发送消息的标准 JmsTemplate 。


<beans:bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
 <beans:property name="environment">
 <beans:props>
 <beans:prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</beans:prop>
 <beans:prop key="java.naming.provider.url">${jms.connectionUrl}</beans:prop>
 <beans:prop key="java.naming.security.principal">${jms.username}</beans:prop>
 <beans:prop key="java.naming.security.credentials">${jms.password}</beans:prop>
 </beans:props>
 </beans:property>
</beans:bean>

<beans:bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
 <beans:property name="jndiTemplate" ref="jndiTemplate"/>
 <beans:property name="jndiName" value="${jms.connectionFactoryName}"/>
</beans:bean>

<beans:bean id="destination" class="org.springframework.jndi.JndiObjectFactoryBean">
 <beans:property name="jndiTemplate" ref="jndiTemplate"/>
 <beans:property name="jndiName" value="${jms.destinationName}"/>
</beans:bean>

<beans:bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
 <beans:property name="connectionFactory" ref="connectionFactory"/>
 <beans:property name="defaultDestination" ref="destination"/>

 <beans:property name="sessionAcknowledgeModeName" value="AUTO_ACKNOWLEDGE"/>
 <beans:property name="sessionTransacted" value="false"/>
</beans:bean>

成功发布的非spring代码的等效位如下。


Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
environment.put(Context.PROVIDER_URL, prop("jms.connectionUrl"));
environment.put(Context.SECURITY_PRINCIPAL, prop("jms.username"));
environment.put(Context.SECURITY_CREDENTIALS, prop("jms.password"));

Context context = new InitialContext(environment);
ConnectionFactory connectionFactory = (ConnectionFactory) context.lookup(prop("jms.connectionFactoryName"));

Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

Destination destination = (Destination) context.lookup(prop("jms.destinationName"));
MessageProducer producer = session.createProducer(destination);
connection.start();

TextMessage tm = session.createTextMessage(UUID.randomUUID().toString());
producer.send(tm);

根据异常消息,它会导致我相信没有在关键位置提供凭据。 JNDI查找成功,并且将任何属性( 包括凭据) 更改为无效值导致在初始化期间引发明确错误。

我最初认为这个问题可以用 UserCredentialsConnectionFactoryAdapter 确保在创建时提供凭据,但导致了相同的错误。 另外,我不需要在上面的简单示例中使用它。

出于完整性考虑,这里是对jmsTemplate代码的描述。


public void sendMessage(final String message) throws JmsException {
 jmsTemplate.send(new MessageCreator() {
 public Message createMessage(Session session) throws JMSException {
 TextMessage textMessage = session.createTextMessage(message);
 return textMessage;
 }
 });
}

最后但不是最重要的是,我没有控制或者访问 web logic实例的地方,我正在发布。 我有一个联系人会检查日志,看看是否有重要线索,但是可以能会有很多时间。

时间: 原作者:

我已经在一些类似的spring Bug 报告研究之后发现了这个问题的( 非直观) 解决。 我决定把这个作为一个答案,而不是删除问题,我希望它能帮助别人在未来。

具体来说,我从 2008找到了 SPR-4720 。 John Bakar推荐一个修补程序,它似乎完全做不到,但它确实解决了上面的问题。

应用于我的项目,修复方式如下所示:


public class UserCredentialsConnectionFactoryAdapter extends org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter {

 private JndiTemplate jndiTemplate;

 public void setJndiTemplate(JndiTemplate jndiTemplate) {
 this.jndiTemplate = jndiTemplate;
 }

 @Override
 protected Connection doCreateConnection(String username, String password) throws JMSException {
 wlFix();
 return super.doCreateConnection(username, password);
 }

/**
 * Associate JNDI variables (user and password) with this thread for the
 * benefit of the WL drivers.
 */
 protected void wlFix() {
 try {
 @SuppressWarnings("unused")
 InitialContext initialContext = new InitialContext(jndiTemplate.getEnvironment());
 } catch (NamingException e) {
 e.printStackTrace();
 }
 }

}

奇怪,对吧?

All-in-all,对于其他人的参考,我的spring 配置现在看起来像这样:


<beans:bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
 <beans:property name="environment">
 <beans:props>
 <beans:prop key="java.naming.factory.initial">${jms.jndiFactory}</beans:prop>
 <beans:prop key="java.naming.provider.url">${jms.connectionUrl}</beans:prop>
 <beans:prop key="java.naming.security.principal">${jms.username}</beans:prop>
 <beans:prop key="java.naming.security.credentials">${jms.password}</beans:prop>
 </beans:props>
 </beans:property>
</beans:bean>

<beans:bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
 <beans:property name="jndiTemplate" ref="jndiTemplate"/>
 <beans:property name="jndiName" value="${jms.connectionFactoryName}"/>
</beans:bean>

<beans:bean id="connectionFactoryAdapter" class="com.company.jms.publisher.UserCredentialsConnectionFactoryAdapter">
 <beans:property name="jndiTemplate" ref="jndiTemplate"/>
 <beans:property name="targetConnectionFactory" ref="connectionFactory"/>
 <beans:property name="username" value="${jms.username}"/>
 <beans:property name="password" value="${jms.password}"/>
</beans:bean>

<beans:bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
 <beans:property name="targetConnectionFactory" ref="connectionFactoryAdapter"/>
 <beans:property name="sessionCacheSize" value="10"/>
</beans:bean>

<beans:bean id="destinationResolver" class="org.springframework.jms.support.destination.JndiDestinationResolver">
 <beans:property name="jndiTemplate" ref="jndiTemplate"/>
</beans:bean>

<beans:bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
 <beans:property name="connectionFactory" ref="cachingConnectionFactory"/>
 <beans:property name="destinationResolver" ref="destinationResolver"/>
 <beans:property name="defaultDestinationName" value="${jms.publish.destinationName}"/>

 <beans:property name="sessionAcknowledgeModeName" value="AUTO_ACKNOWLEDGE"/>
 <beans:property name="sessionTransacted" value="false"/>
</beans:bean>

我愿意向任何能够授权( 无推测) 解释为什么创建虚拟 InitialContext 并解决无法正确提供凭据的人。

原作者:
...