Yes trying to retrieve the SecurityContext from the Message in a separate
interceptor chain won't work. You could try adding an interceptor on the
receiving side to store the Security Context principal so that it's
accessible to your client somehow. I'm not sure if storing it on the
message exchange as opposed to the message might work.

Colm.

On Thu, Sep 13, 2018 at 10:28 AM Burkard Stephan <[email protected]>
wrote:

> Hi Colm
>
> I tried this, but the SecurityContext is always null. I am surely missing
> something.
>
> My web service is configured with the WSS username/password validator:
>
>     endpoint.getInInterceptors().add(wss4JInInterceptor);
>
> endpoint.getProperties().put(SecurityConstants.USERNAME_TOKEN_VALIDATOR,
> wssPasswordValidator);
>
> The "wssPasswordValidator" creates the Spring-Security principal and
> retuns it to CXF as you suggested:
>
>     UsernamePasswordAuthenticationToken token = new
> UsernamePasswordAuthenticationToken(username, password);
>     // Authenticate against Spring-Security
>     Authentication authentication =
> authenticationManager.authenticate(token);
>     SecurityContextHolder.getContext().setAuthentication(authentication);
>     // Pass principal back to CXF
>     credential.setPrincipal(authentication);
>
>
> My client for the downstream backend call is configured to use a custom
> token interceptor:
>
>     Dispatch<Source> dispatch = service.createDispatch(portName,
> Source.class, Service.Mode.PAYLOAD);
>     Client client =
> ((org.apache.cxf.jaxws.DispatchImpl)dispatch).getClient();
>     client.getOutInterceptors().add(new
> MyCustomSecurityTokenInterceptor());
>
> And the token interceptor tries to get the principal from the security
> context (ugly conditionals to show what I get):
>
> protected void addToken(SoapMessage message) {
>     String username = "default";
>     SecurityContext cxfSecurityContext =
> message.get(SecurityContext.class);
>     Principal principal = null;
>
>     if(cxfSecurityContext == null) {
>         logger.info("CXF security context is null");
>     } else {
>         principal = cxfSecurityContext.getUserPrincipal();
>     }
>     if(principal == null) {
>         logger.info("CXF principle is null");
>     } else {
>         if(StringUtils.hasText(principal.getName())) {
>             logger.info("Using username from CXF context: " + username);
>             username = principal.getName();
>         }
>     }
>
> Output:
> CXF security context is null
> CXF principle is null
>
> Do I need to "transport" the SecurityContext somehow from in the incoming
> call to the downstream call?
>
> Thanks
> Stephan
>
>
> -----Ursprüngliche Nachricht-----
> Von: Colm O hEigeartaigh <[email protected]>
> Gesendet: Montag, 13. August 2018 13:07
> An: [email protected]
> Betreff: Re: How to get username of authenticated user to create a token
> for a downstream call
>
> An alternative could be to set the principal on the returned Credential
> object in your validator, for example:
>
>
> credential.setPrincipal(securityContext.getAuthentication().getPrincipal());
>
> CXF will use this to set up a SecurityContext object which returns this
> principal via "securityContext.getUserPrincipal()". This is stored in the
> current message via the key "SecurityContext.class" so you could retrieve
> this object in your interceptor and extract the principal from it.
>
> Colm.
>
> On Tue, Aug 7, 2018 at 4:14 PM, Burkard Stephan <[email protected]
> >
> wrote:
>
> > Yes, you understood it correct.
> >
> > Thanks a lot, I was able to find a solution with the help of your code
> > snippets.
> >
> > Interesting enough: since I use Apache Camel I can simply set an
> > ExchangeProperty on the Camel Exchange and Camel automatically copies
> > it on the CXF RequestContext. Very convenient!
> >
> > In my TokenInterceptor I can read the property in the addToken method
> > and use it to create the token.
> >
> > Thanks a lot
> > Stephan
> >
> >
> > -----Ursprüngliche Nachricht-----
> > Von: Andrei Shakirin <[email protected]>
> > Gesendet: Montag, 6. August 2018 22:45
> > An: [email protected]
> > Betreff: RE: How to get username of authenticated user to create a
> > token for a downstream call
> >
> > Hi,
> >
> > Not 100% sure that I understood your use case correctly.
> > If you have authenticated user in your service implementation from
> > SecurityContext, you can simply set property in client call context
> > and read this property in your client Token interceptor:
> >
> >
> > ((BindingProvider)proxy).getRequestContext().put("thread.local.request
> > .context", "true"); myUser =
> > securityContext.getAuthentication().getPrincipal();
> > ((BindingProvider)proxy).getRequestContext().put("authenticatedUser",
> > myUser);
> >
> >
> > ...
> > public class TokenInterceptor extends
> > AbstractPhaseInterceptor<Message> {
> >
> >
> >   public void handleMessage(Message message)  {
> >       String username = message.getContextualProperty(
> > "authenticatedUser");
> >      ...
> >   }
> >
> > Regards,
> > Andrei.
> >
> > > -----Original Message-----
> > > From: Burkard Stephan [mailto:[email protected]]
> > > Sent: Freitag, 3. August 2018 15:13
> > > To: [email protected]
> > > Subject: How to get username of authenticated user to create a token
> > > for a downstream call
> > >
> > > Hi
> > >
> > > I try to accomplish something I thought is quite a standard use case.
> > > I was probably wrong since I did not found a complete example for it.
> > >
> > > Goal: Build a secured CXF/SpringBoot webservice that calls other
> > > secured webservice(s).
> > > Setup: SpringBoot (1.5.x), CXF (3.1.x), WSS4J (2.1.x), Spring
> > > Security
> > > (4.2.x)
> > >
> > > **What I want to accomplish**
> > >
> > > - My CXF/SpringBoot webservice must authenticate requests against LDAP.
> > >   => I have a working Spring Security setup with
> > > AuthenticationManager, UserDetailsService etc.
> > >   => This setup depends on the Spring SecurityContext.
> > >
> > > - My CXF/SpringBoot webservice must accept WSS Username/Password
> > > (Plaintext).
> > >   => I use a combination of SAAJInInterceptor and WSS4JInInterceptor
> > > (no password callback!) to create a UsernameToken from the WSS header.
> > >   => I use a custom "ws-security.ut.validator" to create a Spring
> > > SecurityContext from the UsernameToken and authenticate the user
> > > against
> > LDAP.
> > >
> > > - My CXF/SpringBoot webservice must call a downstream webservice
> > > that accepts a proprietary token type.
> > >   => This requires another CXF (client) endpoint configuration.
> > >   => I use a custom out-interceptor that extends
> > > AbstractTokenInterceptor to add the proprietary token to the request.
> > >   => However, currently the username is hardcoded because I don't
> > > know where to get it.
> > >
> > > Question: In my Spring SecurityContext I have the authenticated user.
> > > But how can I "hand over" the username to the TokenInterceptor?
> > >
> > > Question: Let's assume I do two downstream calls to finally create
> > > the response for the initial service request. Are these calls
> > > individual "contexts" from a CXF point of view or is there some kind
> > > of "management" around that holds all data of all the calls?
> > >
> > > Thanks
> > > Stephan
> >
> > As a recipient of an email from Talend, your contact personal data
> > will be on our systems. Please see our contacts privacy notice at
> > Talend, Inc. < https://www.talend.com/contacts-privacy-policy/>
> >
> >
> >
>
>
> --
> Colm O hEigeartaigh
>
> Talend Community Coder
> http://coders.talend.com
>


-- 
Colm O hEigeartaigh

Talend Community Coder
http://coders.talend.com

Reply via email to