10 Replies Latest reply on Nov 3, 2014 6:12 PM by juliano.pontes

    Spring Security and Propagating Security Context to EJB?

    pbenedict

      I am porting a webapp from GlassFish 3 to WildFly 8. The webapp uses Spring Security 3.2 and can successfully propagate the security credentials to remote EJBs when running in GlassFish. Evidently, GlassFish is smart enough to see that if my servlet request's getUserPrincipal() is populated (I force that via a servlet filter), it will pick up the Principal and pass it along.

       

      Now that I ported my application to WildFly 8, I no longer get the Principal propagated. I've searched the internet extensively for an answer but cannot find anything that works. I did try the code from this link but login() immediately throws an exception:

      http://www.tapina.com/blog/spring-security-propagate-principal-ejb.html

       

      If I must access custom JBoss classes in my code to set the security context, I am OK with that solution. All I need to do is pass on the Principal and my worries will be over. Please help if you have any input? Thanks so much.

        • 1. Re: Spring Security and Propagating Security Context to EJB?
          jaikiran

          Paul Benedict wrote:

           

          Now that I ported my application to WildFly 8, I no longer get the Principal propagated. I've searched the internet extensively for an answer but cannot find anything that works. I did try the code from this link but login() immediately throws an exception:

          http://www.tapina.com/blog/spring-security-propagate-principal-ejb.html

          Please post that entire exception stacktrace. Also, are you calling on the local interface of an EJB or a remote interface?

          • 2. Re: Spring Security and Propagating Security Context to EJB?
            pbenedict

            I am calling the remote interface. Although the EJBs are currently hosted in the same standalone server today, the plan is to put them into their own container.

             

            So this is the line causing the exception. I am calling this in my servlet filter:

            LoginContext loginContext = new LoginContext("client-login", new MyHandler());
            

             

            Like the link, I set the principal in the callback:

            public class ZHandler implements CallbackHandler {
                @Override
                public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
                    for (Callback callback : callbacks) {
                        if (callback instanceof NameCallback) {
                            final String principal = SecurityContextHolder.getContext().getAuthentication().getName();
                            ((NameCallback) callback).setName(principal);
                        }
                    }
                }
            }
            

             

            And the exception:

            javax.security.auth.login.FailedLoginException: PBOX000070: Password invalid/Password required
                at org.jboss.security.auth.spi.UsernamePasswordLoginModule.login(UsernamePasswordLoginModule.java:284)
                at org.jboss.as.security.RealmDirectLoginModule.login(RealmDirectLoginModule.java:148)
                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 javax.security.auth.login.LoginContext.invoke(LoginContext.java:762)
                at javax.security.auth.login.LoginContext.access$000(LoginContext.java:203)
                at javax.security.auth.login.LoginContext$4.run(LoginContext.java:690)
                at javax.security.auth.login.LoginContext$4.run(LoginContext.java:688)
                at java.security.AccessController.doPrivileged(Native Method)
                at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:687)
                at javax.security.auth.login.LoginContext.login(LoginContext.java:595)
                at com.company.MyCustomerAuthenticationFilter.doFilter(PartnerAnonymousAuthenticationFilter.java:72)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
                at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
                at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
                at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
                at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
                at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
                at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
                at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
                at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
                at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
                at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
                at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
                at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
                at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:56)
                at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:132)
                at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:85)
                at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:61)
                at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
                at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
                at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25)
                at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:113)
                at io.undertow.security.handlers.AuthenticationCallHandler.handleRequest(AuthenticationCallHandler.java:52)
                at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:45)
                at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:61)
                at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:70)
                at io.undertow.security.handlers.SecurityInitialHandler.handleRequest(SecurityInitialHandler.java:76)
                at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25)
                at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
                at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25)
                at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25)
                at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:240)
                at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:227)
                at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:73)
                at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:146)
                at io.undertow.server.Connectors.executeRootHandler(Connectors.java:168)
                at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:687)
                at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
                at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
                at java.lang.Thread.run(Thread.java:744)
            
            • 3. Re: Spring Security and Propagating Security Context to EJB?
              jaikiran

              Paul Benedict wrote:

               

              I am calling the remote interface. Although the EJBs are currently hosted in the same standalone server today, the plan is to put them into their own container.

               

              So this is the line causing the exception. I am calling this in my servlet filter:

              {code}

              LoginContext loginContext = new LoginContext("client-login", new MyHandler());

              {code}

               

              Like the link, I set the principal in the callback:

              {code}

              public class ZHandler implements CallbackHandler {

                  @Override

                  public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

                      for (Callback callback : callbacks) {

                          if (callback instanceof NameCallback) {

                              final String principal = SecurityContextHolder.getContext().getAuthentication().getName();

                              ((NameCallback) callback).setName(principal);

                          }

                      }

                  }

              }

              {code}

               

              And the exception:

              {code}

              javax.security.auth.login.FailedLoginException: PBOX000070: Password invalid/Password required

                  at org.jboss.security.auth.spi.UsernamePasswordLoginModule.login(UsernamePasswordLoginModule.java:284)

                  at org.jboss.as.security.RealmDirectLoginModule.login(RealmDirectLoginModule.java:148)

                  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 javax.security.auth.login.LoginContext.invoke(LoginContext.java:762)

                  at javax.security.auth.login.LoginContext.access$000(LoginContext.java:203)

                  at javax.security.auth.login.LoginContext$4.run(LoginContext.java:690)

                  at javax.security.auth.login.LoginContext$4.run(LoginContext.java:688)

                  at java.security.AccessController.doPrivileged(Native Method)

                  at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:687)

                  at javax.security.auth.login.LoginContext.login(LoginContext.java:595)

                  at com.company.MyCustomerAuthenticationFilter.doFilter(PartnerAnonymousAuthenticationFilter.java:72)

               

              Like I suspected it's not using the "client-login" security domain instead it's falling back to "other" (that's why you see the RealmDirectLoginModule and UsernamePasswordLoginModule in that stacktrace). Have you defined the "client-login" security domain in the configuration? What does it look like?

               

              Edit: I edited this reply since I realized that I was wrong about what I explained earlier. I was thinking of a different issue when I wrote the reply previously.

              • 4. Re: Spring Security and Propagating Security Context to EJB?
                pbenedict

                No, I haven't defined any security domain because I never needed one in the past. As I said, GlassFish was smart enough to get my servlet Principal and pass it down to the EJB layer. I really don't need anything complex; I just want to make JBoss officially aware of the Principal that's attached to my web request. Should I ask for a JBoss enhancement? Also, is it possible for me to manually set the JBoss security context in my code? Perhaps that's the path I should take if JBoss provides such API.

                • 6. Re: Spring Security and Propagating Security Context to EJB?
                  pbenedict

                  Jaikiran, I found the terminology for my use case. It's called "pre-authenticated crdentials" -- a username (principal) and list of roles already known and validated. With pre-authenticated credentials, JBoss doesn't need to authenticate them but just needs to be told about them.

                   

                  However, I don't see how those tests help me. What are you trying to point out to me?

                   

                  And wouldn't the following be closer to the mark? See link:

                  http://stackoverflow.com/questions/10629756/how-to-manually-set-propagate-security-context-information-e-g-principal-for…

                  • 7. Re: Spring Security and Propagating Security Context to EJB?
                    arjant

                    Paul B wrote:

                     

                    Jaikiran, I found the terminology for my use case. It's called "pre-authenticated crdentials" -- a username (principal) and list of roles already known and validated. With pre-authenticated credentials, JBoss doesn't need to authenticate them but just needs to be told about them.

                     

                    This is possibly related to this one: https://issues.jboss.org/browse/SECURITY-746

                     

                    The problem is that JBoss takes a section from the EJB spec that rather vaguely talks about a security domain very literally and always requires such security domain to be setup and consulted. Although this section isn't clear about it, I suspect that the intention is that it only applies to incoming remote calls and it probably originates from the time that EJBs were only remote.

                     

                    When a security context is already established in the web layer, and the web layer calls a local EJB bean, it doesn't make sense for the EJB security interceptor to attempt a login again with a login module. As far as I know, JBoss is the only AS that does it this way.

                    1 of 1 people found this helpful
                    • 8. Re: Spring Security and Propagating Security Context to EJB?
                      pbenedict

                      OK, I have the answer. The first link I provided above is the right stuff. The link, however, neglected the server-side configuration.

                       

                      <subsystem xmlns="urn:jboss:domain:security:1.2">
                      ...
                          <security-domain name="client-login" cache-type="default">
                              <authentication>
                                  <login-module code="org.jboss.security.ClientLoginModule" flag="required"/>
                              </authentication>
                          </security-domain>
                      ...
                      </subsystem>
                      
                      
                      

                       

                      As the link demonstrates, the servlet filter needs to login to this security domain, let the request execute, then logout. This will force JBoss to propogate whatever security credentials you have to the EJB on a per-user/session basis. My reading says the ClientLoginModule performs no important logic except making sure the security domain exists. This means that you can stuff whatever pre-authenticated credentials you want in the required CallbackHandler.

                       

                      PS: Note that your user roles are unavailable for propogation in this solution -- or at least, I haven't figured out a solution to that yet. If I do, I will post again.

                       

                      Some references:

                      [1] https://community.jboss.org/wiki/ClientLoginModule

                      [2] https://docs.jboss.org/jbossas/docs/Server_Configuration_Guide/4/html/Using_JBoss_Login_Modules-ClientLoginModule.html

                      • 9. Re: Spring Security and Propagating Security Context to EJB?
                        arjant

                        The ClienLoginModule may work with getting the user principal and do programmatic role checking from the EJBContext, but I'd be very surprised if it would work with @RolesAllowed.

                         

                        JBoss uses different code paths for these two things.

                        • 10. Re: Spring Security and Propagating Security Context to EJB?
                          juliano.pontes

                          Could you post the entire solution? The link with the filter explanation isn't working...