Can I call secure EJB methods through message-driven beans?

When the user logs in, I need to insert a message into the database (host name, as an example). Since this is just a text message, EJB injection to the client (servlets, JSP, JSF or something else) is completely unnecessary.

The client, in this case, is an authentication filter through which I send the host name to the message-driven bean. Using a message-driven bean, the message is stored in a queue (not in the subject), which is then sent to the EJB by inserting the EJB into this bean.

The strategy described here works great. A problem occurs when an EJB is forced to restrict security. In this case, it throws a security exception.

A message-driven bean is as follows.

@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); } } } 

There is still no one method in stateless EJB that is responsible for inserting a message into the database using the JPA criteria 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. } } 

And the filter that authenticates the user is shown below (in case it needs to be reviewed).

 @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. } 

The protection used here works great elsewhere. The @RolesAllowed(value={"ROLE_ADMIN"}) before the UserStatusBean EJB raises the following exception.

 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) 

It works when this annotation @RolesAllowed(value={"ROLE_ADMIN"}) before removing the EJB.

Can such methods be called using message-driven bean?

I am using GlassFish 4.0.

+6
source share
2 answers

You can use the @RunAs annotation for MDB.

EJB specs say:

A Bean provider can use RunAs or Bean metadata annotations A provider or Application Assembler can use a deployment as a deployment descriptor to determine the startup identifier for a Bean enterprise in a deployment descriptor. The startup identifier as an identifier refers to the Bean enterprise as a whole , that is, to all methods of the bean enterprise, a business interface, home and component interfaces, browsing without an interface, and / or a web service endpoint; message-listening bean methods ; and bean timeout callback methods; and all internal Bean methods so that they can in turn be called.

The distribution of the basic functions of the caller in this case does not matter. It does not matter who called the method and whether the identifier is distributed, the run-as identifier will be used instead, which will be used later.

The run-as identifier must be configured correctly. Basically, if you use the default mapping from principal to roles, you need to create a user, for example. RunAsAdmin and assign it the role ROLE_ADMIN . And then annotate your MDB with @RunAs("RunAsAdmin") .

+2
source

You can try using @RunAs in MDB, for example:

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

In general, however, the EJB specification does not guarantee that the security context will pass through the MDB (see chapter 5.4.13) JSR-318 v. 3.1FR):

The principle of calling the caller can extend to methods for listening to the message bean with the message. Whether this is a function of the specific interface of the message sender and the associated message provider , but is not defined by this specification.

As to whether Glassfish handles this or not, maybe someone can help you.

+1
source

Source: https://habr.com/ru/post/953497/


All Articles