是否可以通过消息驱动的bean调用受保护的EJB的方法?

时间:2013-09-09 15:00:53

标签: java-ee ejb ejb-3.0 jaas message-driven-bean

当用户登录时,我需要在数据库中插入一条消息(主机名,仅作为示例)。由于它只是一条文本消息,因此将EJB注入客户端(Servlets,JSP,JSF或其他东西)是非常必要的。

客户端,在这种情况下,它是一个身份验证过滤器,通过它我将主机名发送到消息驱动的bean。在消息驱动的bean的帮助下,消息存储在队列(而不是主题)中,然后通过将EJB注入此消息传递bean将其提交给EJB。

这里提到的策略运作正常。当EJB被强制实施安全约束时,就会出现问题。在这种情况下,它会引发有关安全性的异常。

消息驱动bean如下所示。

@MessageDriven(mappedName = "jms/destination", activationConfig = {
    @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
})
public class UserStatusMessageBean implements MessageListener
{
    @Resource
    private MessageDrivenContext messageDrivenContext;
    @EJB
    private UserStatusBeanRemote userStatusBeanRemote;

    public UserStatusMessageBean() {}

    @Override
    public void onMessage(Message message)
    {
        TextMessage textMessage;

        try
        {
            if(message instanceof TextMessage)
            {
                textMessage = (TextMessage) message;
                userStatusBeanRemote.addHost(textMessage.getText());
                //This EJB method causes the exception as given below.
            }
            else
            {
                System.out.println("Message is of wrong type : " +message.getClass().getName());
            }
        }
        catch (JMSException e)
        {
            messageDrivenContext.setRollbackOnly();
            System.out.println(e);
        }
        catch (Throwable e)
        {
            System.out.println(e);
        }
    }
}

无状态EJB到目前为止只有一个方法负责使用JPA条件API将消息插入数据库:

@Stateless
@DeclareRoles(value={"ROLE_ADMIN", "ROLE_USER"})
@RolesAllowed(value={"ROLE_ADMIN"})
public class UserStatusBean implements UserStatusBeanRemote
{
    @Override
    public void addHost(String hostName)
    {
         //Business logic to add the host name to the database.
    }
}

以下显示了对用户进行身份验证的过滤器(如果需要进行审核)。

@WebFilter(filterName = "SecurityCheck", urlPatterns = {"/jass/*"})
public final class SecurityCheck implements Filter
{
    @Resource(mappedName="jms/destinationFactory")
    private ConnectionFactory connectionFactory;
    @Resource(mappedName="jms/destination")
    private Queue queue;
    @EJB
    private final UserBeanLocal userService=null;

    public SecurityCheck() {}

    private void sendJMSMessageToDestination(String message) throws JMSException
    {
        Connection connection = null;
        Session session = null;

        try
        {
            connection = connectionFactory.createConnection();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            MessageProducer messageProducer = session.createProducer(queue);
            TextMessage textMessage = session.createTextMessage();
            textMessage.setText(message);
            messageProducer.send(textMessage);
        }
        finally
        {
            if(session!=null){session.close();}
            if(connection!=null){connection.close();}
        }
    }

    private void doBeforeProcessing(ServletRequest request, ServletResponse response) throws IOException, ServletException
    {
        HttpServletRequest httpServletRequest=(HttpServletRequest)request;
        httpServletRequest.login(httpServletRequest.getParameter("userName"), httpServletRequest.getParameter("password"));
    }

    private void doAfterProcessing(ServletRequest request, ServletResponse response) throws IOException, ServletException, JMSException
    {
        HttpServletRequest httpServletRequest=(HttpServletRequest)request;
        HttpServletResponse httpServletResponse=(HttpServletResponse)response;
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        Map<String, Object> sessionMap = externalContext.getSessionMap();

        if(httpServletRequest.isUserInRole("ROLE_USER"))
        {
            sendJMSMessageToDestination(httpServletRequest.getLocalName());//Send a text message through a message-driven bean.
            String userName = httpServletRequest.getParameter("userName");
            UserTable userTable = userService.setLastLogin(userName);
            userTable.setPassword(null);
            sessionMap.put("userName", userTable!=null?userTable.getFirstName():"Unknown");
            sessionMap.put("user", userTable);

            httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
            httpServletResponse.setHeader("Pragma", "no-cache");
            httpServletResponse.setDateHeader("Expires", 0);
            httpServletResponse.sendRedirect("../user_side/Home.jsf");
        }
        else if(httpServletRequest.isUserInRole("ROLE_ADMIN"))
        {
            sendJMSMessageToDestination(httpServletRequest.getLocalName());//Send a text message through a message-driven bean.

            String userName = httpServletRequest.getParameter("userName");
            UserTable userTable = userService.setLastLogin(userName);
            userTable.setPassword(null);
            sessionMap.put("adminName", userTable!=null?userTable.getFirstName():"Unknown");
            sessionMap.put("user", userTable);

            httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
            httpServletResponse.setHeader("Pragma", "no-cache");
            httpServletResponse.setDateHeader("Expires", 0);
            httpServletResponse.sendRedirect("../admin_side/Home.jsf");
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
    {
        try
        {
            doBeforeProcessing(request, response);
        }
        catch (Exception e)
        {
            HttpServletResponse httpServletResponse=(HttpServletResponse)response;
            //FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "Incorrect user name and/or password. Access denied."));
            httpServletResponse.sendRedirect("../utility/Login.jsf");
            return;
        }

        chain.doFilter(request, response);

        try
        {
            doAfterProcessing(request, response);
        }
        catch (JMSException ex)
        {
            Logger.getLogger(SecurityCheck.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    //The rest of this filter.
}

此处应用的安全性可以在其他地方正常工作。 EJB @RolesAllowed(value={"ROLE_ADMIN"})之前的注释UserStatusBean会导致抛出以下异常。

WARNING: EJB5184:A system exception occurred during an invocation on EJB UserStatusBean, method: public void ejb.message.UserStatusBean.addHost(java.lang.String)
WARNING: javax.ejb.AccessLocalException: Client not authorized for this invocation
    at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1895)
    at com.sun.ejb.containers.EJBObjectInvocationHandler.invoke(EJBObjectInvocationHandler.java:204)
    at com.sun.ejb.containers.EJBObjectInvocationHandlerDelegate.invoke(EJBObjectInvocationHandlerDelegate.java:79)
    at $Proxy366.addHost(Unknown Source)
    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:601)
    at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.privateInvoke(StubInvocationHandlerImpl.java:239)
    at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.invoke(StubInvocationHandlerImpl.java:150)
    at com.sun.corba.ee.impl.presentation.rmi.codegen.CodegenStubBase.invoke(CodegenStubBase.java:226)
    at ejb.message.__UserStatusBeanRemote_Remote_DynamicStub.addHost(ejb/message/__UserStatusBeanRemote_Remote_DynamicStub.java)
    at ejb.message._UserStatusBeanRemote_Wrapper.addHost(ejb/message/_UserStatusBeanRemote_Wrapper.java)
    at bean.message.UserStatusMessageBean.onMessage(UserStatusMessageBean.java:37)
    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:601)
    at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1081)
    at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1153)
    at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:4695)
    at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:630)
    at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
    at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:582)
    at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:55)
    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:601)
    at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:883)
    at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
    at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:369)
    at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:4667)
    at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:4655)
    at org.glassfish.ejb.mdb.MessageBeanContainer.deliverMessage(MessageBeanContainer.java:1219)
    at org.glassfish.ejb.mdb.MessageBeanListenerImpl.deliverMessage(MessageBeanListenerImpl.java:81)
    at com.sun.enterprise.connectors.inbound.MessageEndpointInvocationHandler.invoke(MessageEndpointInvocationHandler.java:171)
    at $Proxy406.onMessage(Unknown Source)
    at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java:283)
    at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:107)
    at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.performWork(ThreadPoolImpl.java:497)
    at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:540)

INFO:   javax.ejb.EJBAccessException
    at ejb.message._UserStatusBeanRemote_Wrapper.addHost(ejb/message/_UserStatusBeanRemote_Wrapper.java)
    at bean.message.UserStatusMessageBean.onMessage(UserStatusMessageBean.java:37)
    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:601)
    at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1081)
    at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1153)
    at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:4695)
    at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:630)
    at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
    at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:582)
    at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:55)
    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:601)
    at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:883)
    at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
    at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:369)
    at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:4667)
    at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:4655)
    at org.glassfish.ejb.mdb.MessageBeanContainer.deliverMessage(MessageBeanContainer.java:1219)
    at org.glassfish.ejb.mdb.MessageBeanListenerImpl.deliverMessage(MessageBeanListenerImpl.java:81)
    at com.sun.enterprise.connectors.inbound.MessageEndpointInvocationHandler.invoke(MessageEndpointInvocationHandler.java:171)
    at $Proxy406.onMessage(Unknown Source)
    at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java:283)
    at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:107)
    at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.performWork(ThreadPoolImpl.java:497)
    at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:540)
Caused by: java.rmi.AccessException: CORBA NO_PERMISSION 9998 Maybe; nested exception is: 
    org.omg.CORBA.NO_PERMISSION:   vmcid: 0x2000  minor code: 1806 completed: Maybe
    at com.sun.corba.ee.impl.javax.rmi.CORBA.Util.mapSystemException(Util.java:264)
    at com.sun.corba.ee.impl.javax.rmi.CORBA.Util.wrapException(Util.java:695)
    at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.privateInvoke(StubInvocationHandlerImpl.java:249)
    at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.invoke(StubInvocationHandlerImpl.java:150)
    at com.sun.corba.ee.impl.presentation.rmi.codegen.CodegenStubBase.invoke(CodegenStubBase.java:226)
    at ejb.message.__UserStatusBeanRemote_Remote_DynamicStub.addHost(ejb/message/__UserStatusBeanRemote_Remote_DynamicStub.java)
    ... 30 more
Caused by: org.omg.CORBA.NO_PERMISSION:   vmcid: 0x2000  minor code: 1806 completed: Maybe
Caused by: javax.ejb.AccessLocalException: Client not authorized for this invocation
    at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1895)
    at com.sun.ejb.containers.EJBObjectInvocationHandler.invoke(EJBObjectInvocationHandler.java:204)
    at com.sun.ejb.containers.EJBObjectInvocationHandlerDelegate.invoke(EJBObjectInvocationHandlerDelegate.java:79)
    at $Proxy366.addHost(Unknown Source)
    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:601)
    at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.privateInvoke(StubInvocationHandlerImpl.java:239)
    at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.invoke(StubInvocationHandlerImpl.java:150)
    at com.sun.corba.ee.impl.presentation.rmi.codegen.CodegenStubBase.invoke(CodegenStubBase.java:226)
    at ejb.message.__UserStatusBeanRemote_Remote_DynamicStub.addHost(ejb/message/__UserStatusBeanRemote_Remote_DynamicStub.java)
    at ejb.message._UserStatusBeanRemote_Wrapper.addHost(ejb/message/_UserStatusBeanRemote_Wrapper.java)
    at bean.message.UserStatusMessageBean.onMessage(UserStatusMessageBean.java:37)
    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:601)
    at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1081)
    at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1153)
    at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:4695)
    at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:630)
    at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
    at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:582)
    at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:55)
    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:601)
    at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:883)
    at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
    at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:369)
    at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:4667)
    at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:4655)
    at org.glassfish.ejb.mdb.MessageBeanContainer.deliverMessage(MessageBeanContainer.java:1219)
    at org.glassfish.ejb.mdb.MessageBeanListenerImpl.deliverMessage(MessageBeanListenerImpl.java:81)
    at com.sun.enterprise.connectors.inbound.MessageEndpointInvocationHandler.invoke(MessageEndpointInvocationHandler.java:171)
    at $Proxy406.onMessage(Unknown Source)
    at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java:283)
    at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:107)
    at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.performWork(ThreadPoolImpl.java:497)
    at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:540)

在删除EJB之前注释@RolesAllowed(value={"ROLE_ADMIN"})时,它可以正常工作。

是否可以通过消息驱动的bean调用此类方法?

我正在使用GlassFish 4.0。

2 个答案:

答案 0 :(得分:2)

您可以将@RunAs注释用于MDB。

EJB规范说:

  

Bean Provider可以使用RunAs元数据批注或Bean   提供者或应用程序汇编程序可以使用run-as部署   描述符元素,用于定义企业bean的run-as标识   在部署描述符中。 run-as身份适用于   企业bean作为一个整体,即企业的所有方法   bean的业务,家庭和组件接口,无接口视图,   和/或Web服务端点; 到一个消息监听器的方法   消息驱动的bean ;和一个超时的回调方法   企业豆;以及它们可能的bean的所有内部方法   反过来打电话。

在这种情况下,来电主要传播并不重要。调用该方法以及是否传播身份是不相关的,将使用run-as身份代替并进一步传播。

但是,必须正确设置run-as标识。基本上,如果您使用默认主体到角色映射,则需要创建一个用户,例如RunAsAdmin并为他指定ROLE_ADMIN角色。然后使用@RunAs("RunAsAdmin")注释您的MDB。

答案 1 :(得分:1)

您可以尝试在MDB上使用@RunAs,如下所示:

@MessageDriven(...)
@RunAs("ROLE_ADMIN")
public class UserStatusMessageBean implements MessageListener {

但是,一般而言,EJB规范并不保证安全上下文将通过MDB(参见JSR-318 v.3.1FR第5.4.13章):

  

调用者主体可以传播到消息驱动bean的消息监听器方法中。 是否发生这种情况是特定消息监听器接口和相关消息传递提供程序的功能,但不受此规范的约束。

至于Glassfish是否处理这个问题,也许其他人可以帮助你。