11 Replies Latest reply on Oct 8, 2010 7:33 AM by lazo

    WebService and seam authentication filter

    lazo

      I have ejb web service and want to use seam authentication filter. My WS is defined as


      @Stateless
      @Name("myWs")
      @WebService(name = "myWs", serviceName = "myWs")
      @WebContext(
        virtualHosts="vh1",
        contextRoot="/services"
      )
      @Restrict("#{identity.loggedIn}")
      public class MyWs implements MyWsRemote {
        @In
        EntityManager entityManager;
        ...



      In components.xml I have enabled seam authentication filter


      <web:authentication-filter url-pattern="/*" auth-type="basic" />



      Application is packed as EAR archive, myWs is in application-ejb.jar, web package of application is in same virtual host and has / (root) contextRoot.


      Authentication filter is intercepting all jsf and other servlets defined in application.war.


      Am I missing something ?


      Is there any another way to use seam logic for basic authentication.


      Thanks.

        • 1. Re: WebService and seam authentication filter
          lazo

          forgot versions:


          seam 2.1.2 and jBoss 5.1

          • 2. Re: WebService and seam authentication filter
            shane.bryzak

            Are you getting an exception, or is the interceptor just not working?  Make sure you have the SOAPRequestHandler configured as described in the docs - http://docs.jboss.org/seam/2.2.0.GA/reference/en-US/html/webservices.html#d0e21941


            Also, you might consider using the web service class itself as just a facade and using it to invoke the Seam components containing your business logic (and also annotated with @Restrict).

            • 3. Re: WebService and seam authentication filter
              lazo

              I have SOAPRequestHandler and seam injection is working (em is injected).


              If I use

              @Restrict("#{identity.loggedIn}")

              the I get org.jboss.seam.security.NotLoggedInException


              If I remove

              @Restrict("#{identity.loggedIn}")

              then the service is serving requests without restriction and without exceptions.


              Scenarios are the same if I send correct username and pass or no.



              I'm planing to create it just as facade, but as I understand this has nothing to do with authentication filter ... right ?


              Thanks.


              • 4. Re: WebService and seam authentication filter
                lazo

                After research I found out that even org.jboss.seam.servlet.SeamFilter is not intercepting the soap request. My web.xml:


                 <filter>
                    <filter-name>Seam Filter</filter-name>
                    <filter-class>org.jboss.seam.servlet.SeamFilter</filter-class>
                    <init-param>
                      <param-name>createTempFiles</param-name>
                      <param-value>true</param-value>
                    </init-param>
                  </filter>
                
                  <filter-mapping>
                    <filter-name>Seam Filter</filter-name>
                    <url-pattern>/*</url-pattern>
                  </filter-mapping>



                Is this due to different contextRoots ?

                • 5. Re: WebService and seam authentication filter
                  shane.bryzak

                  That's a bit weird, a call to identity.isLoggedIn() shouldn't be throwing a NotLoggedInException, it should either return true if the user is logged in or false otherwise, so I don't know what's going on there - you might need to do some investigation with a debugger.


                  About the filter, yep you're right it won't intercept the soap request as it is processed by the JBossWS servlet assuming you're using it (and therefore has a different servlet stack).

                  • 6. Re: WebService and seam authentication filter
                    lazo

                    Yes, I'm using JbossWS, so I cannot use seam authentication filter, right ?


                    Is there any other way to use basic authentication with seam ? Or how can I use identity.isLoggedIn() without http session?


                    One way is using technique like extracting conversationId, but this seems more workaround than best practice.



                    Thanks.



                    stack trace of exception mentioned earlier


                    15:08:58,464 DEBUG [Identity] Error evaluating expression [#{identity.loggedIn}] - User not logged in
                    15:08:58,464 WARN  [StatelessBeanContext] EJBTHREE-1337: do not get WebServiceContext property from stateless bean context, it should already have been injected
                    15:08:58,464 DEBUG [EjbSynchronizations] afterCompletion
                    15:08:58,479 ERROR [SOAPFaultHelperJAXWS] SOAP request exception
                    org.jboss.seam.security.NotLoggedInException
                            at org.jboss.seam.security.Identity.checkRestriction(Identity.java:217)
                            at org.jboss.seam.security.SecurityInterceptor$Restriction.check(SecurityInterceptor.java:113)
                            at org.jboss.seam.security.SecurityInterceptor.aroundInvoke(SecurityInterceptor.java:160)
                            at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                            at org.jboss.seam.transaction.RollbackInterceptor.aroundInvoke(RollbackInterceptor.java:28)
                            at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                            at org.jboss.seam.core.BijectionInterceptor.aroundInvoke(BijectionInterceptor.java:77)
                            at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                            at org.jboss.seam.core.MethodContextInterceptor.aroundInvoke(MethodContextInterceptor.java:44)
                            at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                            at org.jboss.seam.persistence.EntityManagerProxyInterceptor.aroundInvoke(EntityManagerProxyInterceptor.java:29)
                            at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                            at org.jboss.seam.persistence.HibernateSessionProxyInterceptor.aroundInvoke(HibernateSessionProxyInterceptor.java:30)
                            at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                            at org.jboss.seam.intercept.RootInterceptor.invoke(RootInterceptor.java:107)
                            at org.jboss.seam.intercept.SessionBeanInterceptor.aroundInvoke(SessionBeanInterceptor.java:50)
                            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                            at java.lang.reflect.Method.invoke(Method.java:597)
                            at org.jboss.ejb3.interceptors.aop.EJB3InterceptorInterceptor.invoke(EJB3InterceptorInterceptor.java:83)
                            at org.jboss.ejb3.interceptors.aop.EJB3InterceptorInterceptor.invoke(EJB3InterceptorInterceptor.java:70)
                            at org.jboss.ejb3.EJBContainerInvocationWrapper.invokeNext(EJBContainerInvocationWrapper.java:59)
                            at org.jboss.ejb3.interceptors.aop.InterceptorSequencer.invoke(InterceptorSequencer.java:73)
                            at org.jboss.ejb3.interceptors.aop.InterceptorSequencer.aroundInvoke(InterceptorSequencer.java:59)
                            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                            at java.lang.reflect.Method.invoke(Method.java:597)
                            at org.jboss.aop.advice.PerJoinpointAdvice.invoke(PerJoinpointAdvice.java:174)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor.fillMethod(InvocationContextInterceptor.java:72)
                            at org.jboss.aop.advice.org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor_z_fillMethod_8119943.invoke(InvocationContextInterceptor_z_fillMethod_8119943.java)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor.setup(InvocationContextInterceptor.java:88)
                            at org.jboss.aop.advice.org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor_z_setup_8119943.invoke(InvocationContextInterceptor_z_setup_8119943.java)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.ejb3.connectionmanager.CachedConnectionInterceptor.invoke(CachedConnectionInterceptor.java:62)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.ejb3.entity.TransactionScopedEntityManagerInterceptor.invoke(TransactionScopedEntityManagerInterceptor.java:56)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.ejb3.AllowedOperationsInterceptor.invoke(AllowedOperationsInterceptor.java:47)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.ejb3.tx.NullInterceptor.invoke(NullInterceptor.java:42)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.ejb3.stateless.StatelessInstanceInterceptor.invoke(StatelessInstanceInterceptor.java:68)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:79)
                            at org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:190)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.ejb3.tx.NullInterceptor.invoke(NullInterceptor.java:42)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.ejb3.security.RoleBasedAuthorizationInterceptorv2.invoke(RoleBasedAuthorizationInterceptorv2.java:201)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.ejb3.security.Ejb3AuthenticationInterceptorv2.invoke(Ejb3AuthenticationInterceptorv2.java:186)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:41)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.ejb3.BlockContainerShutdownInterceptor.invoke(BlockContainerShutdownInterceptor.java:67)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.aspects.currentinvocation.CurrentInvocationInterceptor.invoke(CurrentInvocationInterceptor.java:67)
                            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
                            at org.jboss.ejb3.stateless.StatelessContainer.localInvoke(StatelessContainer.java:306)
                            at org.jboss.ejb3.stateless.StatelessContainer.invokeEndpoint(StatelessContainer.java:662)
                            at org.jboss.wsf.container.jboss50.invocation.InvocationHandlerEJB3.invoke(InvocationHandlerEJB3.java:96)
                            at org.jboss.ws.core.server.ServiceEndpointInvoker.invoke(ServiceEndpointInvoker.java:222)
                            at org.jboss.wsf.stack.jbws.RequestHandlerImpl.processRequest(RequestHandlerImpl.java:474)
                            at org.jboss.wsf.stack.jbws.RequestHandlerImpl.handleRequest(RequestHandlerImpl.java:295)
                            at org.jboss.wsf.stack.jbws.RequestHandlerImpl.doPost(RequestHandlerImpl.java:205)
                            at org.jboss.wsf.stack.jbws.RequestHandlerImpl.handleHttpRequest(RequestHandlerImpl.java:131)
                            at org.jboss.wsf.common.servlet.AbstractEndpointServlet.service(AbstractEndpointServlet.java:85)
                            at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
                            at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
                            at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                            at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
                            at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                            at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                            at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:235)
                            at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
                            at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:190)
                            at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:92)
                            at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.process(SecurityContextEstablishmentValve.java:126)
                            at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:70)
                            at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
                            at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
                            at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
                            at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:330)
                            at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:829)
                            at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:598)
                            at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
                            at java.lang.Thread.run(Thread.java:619)
                    15:08:58,479 DEBUG [Lifecycle] After request, destroying contexts
                    

                    • 7. Re: WebService and seam authentication filter
                      shane.bryzak

                      Oops, just ignore me on the identity.isLoggedIn() bit.. of course it will throw an exception if it's in a @Restrict element.  As for authenticating, there's a couple of options - either you can make two WS calls, the first one to authenticate and the second one to invoke your method (however this will require cookie support in your WS client).  The other option is to pass the credentials in with your other method parameters, and perform an inline authentication check before executing your business logic.  Either option is not ideal, I know, however we don't have support for WS-Security in Seam.

                      • 8. Re: WebService and seam authentication filter
                        lazo

                        I'm combining SOAPRequestHandler and seam Authentication filter in CustomSOAPRequestHandler to suport basic authentication.


                        Then my clients wont need cookie support neither my methods will require additional parameters.


                        For now it seems to work, just some final tunings are missing.


                        Thanks for help again.

                        • 9. Re: WebService and seam authentication filter
                          lazo

                          If someone has similar problems, here is my solution:


                          /**
                           * Provides seam based basic authentication.
                           * 
                           * @author Matej Lazar
                           *
                           */
                          public class CustomSOAPRequestHandler extends SOAPRequestHandler {
                               /**
                                * The QName of the conversation ID element in the SOAP request header
                                */
                               public static final QName CIDQN = new QName("http://www.jboss.org/seam/webservice", "conversationId", "seam");
                          
                               private static final LogProvider log = Logging.getLogProvider(SOAPRequestHandler.class);   
                          
                               /**
                                * Inbound message handler. Seam contexts should be initialized here, and
                                * the conversation ID (if present) is extracted from the request.
                                * 
                                * @param messageContext The message context
                                * @return boolean true if processing should continue
                                */
                               @Override
                               public boolean handleInbound(final MessageContext messageContext) {
                                    try {
                                         HttpServletRequest request = (HttpServletRequest) messageContext.get(MessageContext.SERVLET_REQUEST);      
                                         HttpServletResponse $response = (HttpServletResponse) messageContext.get(MessageContext.SERVLET_RESPONSE);
                          
                                         ServletLifecycle.beginRequest(request);
                                         ServletContexts.instance().setRequest(request);
                          
                                         String conversationId = extractConversationId(messageContext);
                                         ConversationPropagation.instance().setConversationId( conversationId );
                                         Manager.instance().restoreConversation();
                          
                                         ServletLifecycle.resumeConversation(request);             
                          
                                         if (!processBasicAuth(request, $response)) {
                                              return false;
                                         }
                                         return true;
                                    } catch (SOAPException ex) {
                                         log.error("Error handling inbound SOAP request", ex);
                                         return false;
                                    }
                               }
                          
                          
                               /**
                                * @see org.jboss.seam.web.processBasicAuth
                                * 
                                * @param request
                                * @param response
                                * @return
                                * @throws IOException
                                */
                               private boolean processBasicAuth(final HttpServletRequest request, final HttpServletResponse response) {
                                    //Context ctx = new SessionContext( new ServletRequestSessionMap(request) );
                                    //Identity identity = (Identity) ctx.get(Identity.class);
                                    Identity identity = Identity.instance();
                          
                                    //Credentials credentials = (Credentials) ctx.get(Credentials.class);
                                    Credentials credentials = (Credentials) Component.getInstance(Credentials.class);
                          
                                    boolean requireAuth = false;
                                    String header = request.getHeader("Authorization");
                                    if (header != null && header.startsWith("Basic ")) {
                                         String base64Token = header.substring(6);
                                         String token = new String(Base64.decode(base64Token));
                          
                                         String username = "";
                                         String password = "";
                                         int delim = token.indexOf(":");
                          
                                         if (delim != -1) {
                                              username = token.substring(0, delim);
                                              password = token.substring(delim + 1);
                                         }              
                          
                                         // Only reauthenticate if username doesn't match Identity.username and user isn't authenticated
                                         if (!username.equals(credentials.getUsername()) || !identity.isLoggedIn()) {
                                              try {
                                                   credentials.setPassword(password);
                                                   authenticate( request, username );
                                              } catch (Exception ex) {
                                                   log.warn("Error authenticating: " + ex.getMessage());
                                                   requireAuth = true;
                                              }  
                                         }
                                    }
                          
                                    if (!identity.isLoggedIn() && !credentials.isSet()) {
                                         requireAuth = true;
                                    }
                          
                                    if ((requireAuth && !identity.isLoggedIn())) {
                                         log.debug("Sending 401 error ...");
                                         response.addHeader("WWW-Authenticate", "Basic realm=\"seamApp\"");
                                         try {
                                              response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Not authorized");
                                         } catch (IOException e) {
                                              log.error("Error sending 401: " + e);
                                         }  
                                         return false;
                                    }               
                                    return true;
                               }
                          
                               /**
                                * @see org.jboss.seam.web.authenticate
                                * 
                                * @param request
                                * @param response
                                * @return
                                * @throws IOException
                                */
                               private void authenticate(final HttpServletRequest request, final String username) throws ServletException, IOException, LoginException {
                                    Identity identity = Identity.instance();
                                    identity.getCredentials().setUsername(username);
                                    identity.authenticate();
                               }
                               
                               /**
                                * Copied from SOAPRequestHandler because it is defined as private.
                                * 
                                * -- original comment --
                                * Extracts the conversation ID from an incoming SOAP message
                                * 
                                * @param messageContext
                                * @return The conversation ID, or null if there is no conversation ID set
                                * @throws SOAPException
                                */
                               private String extractConversationId(final MessageContext messageContext) throws SOAPException {
                                    SOAPMessageContext smc = (SOAPMessageContext) messageContext;
                                    SOAPHeader header = smc.getMessage().getSOAPHeader();
                          
                                    if (header != null) {
                                         Iterator iter = header.getChildElements(CIDQN);
                                         if (iter.hasNext())     {
                                              SOAPElement element = (SOAPElement) iter.next();
                                              return element.getFirstChild().getNodeValue();
                                         }
                                    }
                                    return null;
                               }
                          }
                          

                          • 10. Re: WebService and seam authentication filter
                            jigneshmpatel

                            Curiosity which version of web services you did used?
                            1.JBossWS native or CXF or Metro?

                            • 11. Re: WebService and seam authentication filter
                              lazo

                              JBossWS native.