2012-12-04 24 views
5

Sto utilizzando questo fantastico plug-in, http://grails.org/plugin/cxf-client, per consumare un primo servizio web con sicurezza.Come fornire credenziali dinamiche (nome utente e password) al servizio web utilizzando il plugin Grails-cxf

Così ho già avere qualcosa di simile nel mio config:

cxf { 
    client { 
    cybersourceClient {   
     clientInterface = com.webhost.soapProcessor 
     serviceEndpointAddress = "https://webhost/soapProcessor" 
     wsdl = "https://webhost/consumeMe.wsdl" 
     secured = true 
     username = "myUname" 
     password = "myPwd" 
    } 
} 

Questo funziona davvero bene, ma quello che vorrei fare ora è quello di fornire i miei utenti la possibilità di inserire un nome utente e una password in modo possono inserire il nome utente e la password per utilizzare il servizio. Qualcuno sa come fare questo?

Ho il sospetto che si sta usando un personalizzato In Interceptor come nel progetto demo:

package com.cxf.demo.security 

import com.grails.cxf.client.CxfClientInterceptor 

import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor 
import org.apache.ws.security.WSPasswordCallback 
import org.apache.ws.security.handler.WSHandlerConstants 

import javax.security.auth.callback.Callback 
import javax.security.auth.callback.CallbackHandler 
import javax.security.auth.callback.UnsupportedCallbackException 


class CustomSecurityInterceptor implements CxfClientInterceptor { 

String pass 
String user 


    WSS4JOutInterceptor create() { 
    Map<String, Object> outProps = [:] 
    outProps.put(WSHandlerConstants.ACTION, org.apache.ws.security.handler.WSHandlerConstants.USERNAME_TOKEN) 
    outProps.put(WSHandlerConstants.USER, user) 
    outProps.put(WSHandlerConstants.PASSWORD_TYPE, org.apache.ws.security.WSConstants.PW_TEXT) 
    outProps.put(WSHandlerConstants.PW_CALLBACK_REF, new CallbackHandler() { 

     void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { 
      WSPasswordCallback pc = (WSPasswordCallback) callbacks[0] 
      pc.password = pass 
      pc.identifier = user 
     } 
    }) 

    new WSS4JOutInterceptor(outProps) 
} 
} 

Ma siccome io non un'istanza di questo intercettore, o capire come è istanziato, non so come posso ottenere le credenziali dell'utente utilizzate nell'intercettore.

Qualcuno sa come fare questo/avere un codice di esempio?

Grazie!

risposta

0

supponendo che si sta utilizzando il plugin Primavera di sicurezza, e le credenziali WS che si desidera utilizzare sono le proprietà del vostro oggetto User dominio, quindi qualcosa di simile dovrebbe funzionare (non testata):

src/Groovy/com /cxf/demo/security/CustomSecurityInterceptor.groovy

package com.cxf.demo.security 

import com.grails.cxf.client.CxfClientInterceptor 

import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor 
import org.apache.ws.security.WSPasswordCallback 
import org.apache.ws.security.handler.WSHandlerConstants 

import javax.security.auth.callback.Callback 
import javax.security.auth.callback.CallbackHandler 
import javax.security.auth.callback.UnsupportedCallbackException 


class CustomSecurityInterceptor implements CxfClientInterceptor { 

    def springSecurityService 
    def grailsApplication 

    WSS4JOutInterceptor create() { 
    Map<String, Object> outProps = [:] 
    outProps.put(WSHandlerConstants.ACTION, org.apache.ws.security.handler.WSHandlerConstants.USERNAME_TOKEN) 
    // take default username from config 
    outProps.put(WSHandlerConstants.USER, grailsApplication.config.cxf.client.cybersourceClient.username) 
    outProps.put(WSHandlerConstants.PASSWORD_TYPE, org.apache.ws.security.WSConstants.PW_TEXT) 
    outProps.put(WSHandlerConstants.PW_CALLBACK_REF, new CallbackHandler() { 

     void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { 
      WSPasswordCallback pc = (WSPasswordCallback) callbacks[0] 
      // take password from current user, fall back to config if no 
      // user currently logged in/not in a request thread, etc. 
      pc.password = (springSecurityService.currentUser?.wsPassword 
       ?: grailsApplication.config.cxf.client.cybersourceClient.password) 
     } 
    }) 

    new CustomWSS4JOutInterceptor(springSecurityService, outProps) 
    } 
} 

class CustomWSS4JOutInterceptor extends WSS4JOutInterceptor { 
    def springSecurityService 

    CustomWSS4JOutInterceptor(springSecurityService, outProps) { 
    super(outProps) 
    this.springSecurityService = springSecurityService 
    } 

    // overridden to fetch username dynamically from logged in user 
    // but fall back on config if no user/not on a request hander thread 
    public Object getOption(String key) { 
    if(key == WSHandlerConstants.USER && springSecurityService.currentUser?.wsUser) { 
     return springSecurityService.currentUser?.wsUser 
    } else return super.getOption(key) 
    } 
} 

graal-app/conf/primavera/resources.groovy

import com.cxf.demo.security.CustomSecurityInterceptor 
beans = { 
    customSecurityInterceptor(CustomSecurityInterceptor) { 
    springSecurityService = ref('springSecurityService') 
    grailsApplication = ref('grailsApplication') 
    } 
} 

e nella configurazione, sostituire secured = true con securityInterceptor = 'customSecurityInterceptor'

Lo stesso modello funziona se non si utilizza Primavera di sicurezza. I bit cruciali sono il gestore di callback

  pc.password = (springSecurityService.currentUser?.wsPassword 
       ?: grailsApplication.config.cxf.client.cybersourceClient.password) 

e la logica nome utente nel getOption

if(key == WSHandlerConstants.USER && springSecurityService.currentUser?.wsUser) { 
     return springSecurityService.currentUser?.wsUser 

Ad esempio, se il nome utente e la password vengono memorizzate nella sessione HTTP allora al posto del springSecurityService è possibile utilizzare la Spring RequestContextHolder, il cui metodo statico getRequestAttributes() restituisce GrailsWebRequest gestito dal thread corrente, oppure null se il thread corrente non sta elaborando una richiesta (ad esempio se si tratta di un lavoro in background).

RequestContextHolder.requestAttributes?.session?.wsUser 

O se sono attributi di richiesta (vale a dire che hai detto request.wsUser = 'realUsername' nel regolatore) è possibile utilizzare RequestContextHolder.requestAttributes?.currentRequest?.wsUser.

+0

Grazie per la tua response-, ma ho lavorato il modo groovier di farlo: D – lilalfyalien

+0

grazie- che ne dici se mi basta usare una fabbrica per specifici della sessione CustomSecurityInterceptors produrre? – lilalfyalien

+0

@lilalfyalien Ho aggiunto qualche spiegazione in più su "RequestContextHolder". –

0

Ecco una risposta generica per chiunque altro:

1. Config.Groovy

cxf { 
    client { 

     nameOfClient { 
      clientInterface = com.webhost.soapProcessor 
      serviceEndpointAddress = "https://webhost/soapProcessor" 
      wsdl = "https://webhost/soapProcessorconsumeMe.wsdl" 
      secured = true 
      securityInterceptor = "nameOfSecurityInterceptorBean" 
     } 
    } 
} 

2. Resources.groovy

import com.company.package.MySecurityInterceptor 

beans = { 

nameOfSecurityInterceptorBean(MySecurityInterceptor) { 
} 

} 

3. Crea una MySecurityInterceptor sotto com.company.package

package com.company.package; 

import com.grails.cxf.client.CxfClientInterceptor 

import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor 
import org.apache.ws.security.WSPasswordCallback 
import org.apache.ws.security.handler.WSHandlerConstants 

import javax.security.auth.callback.Callback 
import javax.security.auth.callback.CallbackHandler 
import javax.security.auth.callback.UnsupportedCallbackException 

import org.springframework.web.context.request.RequestContextHolder 

class MySecurityInterceptor implements CxfClientInterceptor { 


WSS4JOutInterceptor create() { 
    Map<String, Object> outProps = [:] 
    outProps.put(WSHandlerConstants.ACTION, org.apache.ws.security.handler.WSHandlerConstants.USERNAME_TOKEN) 
    outProps.put(WSHandlerConstants.USER, user) 
    outProps.put(WSHandlerConstants.PASSWORD_TYPE, org.apache.ws.security.WSConstants.PW_TEXT) 
    outProps.put(WSHandlerConstants.PW_CALLBACK_REF, new CallbackHandler() { 

       void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { 
        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0] 
        def requestObj = RequestContextHolder.requestAttributes?.currentRequest 
        pc.password = requestObj.soapPassword 
        pc.identifier = requestObj.soapIdentifier 
       } 
      }) 

    new WSS4JOutInterceptor(outProps) 
} 
} 

4. Ora abbiamo bisogno di inserire il nome utente e la password nella richiesta (thread-safe) per essere pulle d dal intercettore:

import com.company.package.MySecurityInterceptor 
    class MySoapSendingController { 

    SoapProcessor nameOfClient 


    def index() { 

    request['soapIdentifier'] = "usernameToUse" 
    request['soapPassword'] = "passwordToUse" 


       ... 

     ReplyMessage replyMsg = nameOfClient.makeSOAPRequest(request) 
     } 
    } 
+1

Questo non è thread-safe: se due diversi utenti eseguono entrambi l'azione del controller nello stesso momento in cui potresti finire con uno di loro utilizzando le credenziali dell'altro, o peggio, entrambi cercano di usare la password di un utente con il nome utente di l'altro ... –

+0

Dannazione! Non sto usando SpringSecurity quindi ci sono altre opzioni? – lilalfyalien

+0

C'è un modo per mantenere una sorta di array semi-permanente di utenti nel mio intercettore con nomi utente e password per ognuno? – lilalfyalien