2012-12-04 21 views
5

Używam tej niesamowitej wtyczki, http://grails.org/plugin/cxf-client, do korzystania z pierwszej usługi serwisowej z zabezpieczeniami.Jak zapewnić dynamiczne poświadczenia (nazwa użytkownika i hasło) do usługi sieciowej za pomocą wtyczki Grails-cxf

Więc mam już coś takiego w moim config:

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

To działa bardzo dobrze, ale to, co chciałbym teraz zrobić jest zapewnienie moje użytkownikom możliwość, aby wprowadzić nazwę użytkownika i hasło, mogą wpisać swoją nazwę użytkownika i hasło, aby skorzystać z usługi. Czy ktoś wie, jak to zrobić?

Podejrzewam, że to przy użyciu niestandardowego w Interceptor jak w projekcie 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) 
} 
} 

Ale jak nie instancję tej przechwytujących lub zrozumieć jak to instancja, nie wiem, jak mogę dostać poświadczenia użytkownika używane w przechwytywaczu.

Czy ktoś wie, jak to zrobić/masz jakiś przykładowy kod?

Dzięki!

Odpowiedz

0

Zakładając, że używasz wtyczki Wiosna Zabezpieczenia, a poświadczenia WS chcesz użyć są właściwości obiektu User swojej domeny, a następnie coś jak to powinno działać (niesprawdzone):

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

Grails app/conf/źródło/resources.groovy

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

iw konfiguracji, wymienić secured = true z securityInterceptor = 'customSecurityInterceptor'

Ten sam wzór będzie działać, jeśli nie używasz Wiosna Security. Decydujące bity są handler zwrotna

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

i logika nazwa_użytkownika w getOption

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

Na przykład, jeśli nazwa użytkownika i hasło są przechowywane w sesji HTTP następnie zamiast springSecurityService można użyć Spring RequestContextHolder, którego metoda statyczna getRequestAttributes() zwraca GrailsWebRequest obsługiwany przez bieżący wątek lub null, jeśli bieżący wątek nie przetwarza żądania (np. Jeśli jest to praca w tle).

RequestContextHolder.requestAttributes?.session?.wsUser 

Albo jeśli są one atrybuty żądanie (tzn mówiłeś request.wsUser = 'realUsername' w sterowniku) można użyć RequestContextHolder.requestAttributes?.currentRequest?.wsUser.

+0

Dzięki za response- ale wypracowaliśmy Groovier sposób to zrobić: D – lilalfyalien

+0

Thanks- jak o tym, czy po prostu użyć fabrykę sesyjnych specyficznego CustomSecurityInterceptors produkują? – lilalfyalien

+0

@lilalfyalien Dodałem trochę więcej wyjaśnień 'RequestContextHolder'. –

0

Oto odpowiedź rodzajowy dla nikogo innego:

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. Utwórz MySecurityInterceptor pod 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. Teraz musimy wpisz nazwę użytkownika i hasło w żądaniu (wątek bezpieczny), aby był pulle d przez kolektora:

import com.company.package.MySecurityInterceptor 
    class MySoapSendingController { 

    SoapProcessor nameOfClient 


    def index() { 

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


       ... 

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

To nie jest bezpieczne dla wątków - jeśli dwóch różnych użytkowników jednocześnie wykona akcję kontrolera w tym samym czasie, może się zdarzyć, że jeden z nich użyje poświadczeń drugiej osoby lub, co gorsza, obydwoje próbują użyć hasła jednego użytkownika z nazwą użytkownika drugi ... –

+0

Cholera! Nie używam SpringSecurity, więc czy są jakieś inne opcje? – lilalfyalien

+0

Czy istnieje sposób na utrzymanie pewnego rodzaju półtrwałej tablicy użytkowników w moim przechwytywaczu z nazwami użytkowników i hasłami dla każdego z nich? – lilalfyalien