2

I'm constructing a Camel route that will make calls to Twilio's SMS API to send SMSs. The API demands BASIC authentication but my calls are failing with a 401 authentication error. I've copied my config from this worked example and validated that my credentials are sound by curl-ing the call. However, I can't work out what I'm missing on the Camel side.

My Camel HTTP integration with APIs that don't enforce authentication all work fine and the URL for the Twilio call is being fed correctly (using the header *Exchange.HTTP_QUERY*).

Any help much appreciated. Thanks.

Here's my Blueprint-OSGi Camel route:-

<camelContext id="jellyfish-messaging-sms" errorHandlerRef="deadLetterQueue"
    trace="false" xmlns="http://camel.apache.org/schema/blueprint">

     <route id="sms.twilio">
        <from uri="activemq:sms.twilio" />

        <setHeader headerName="Content-Type">
            <constant>application/x-www-form-urlencoded</constant>
        </setHeader>
        <setHeader headerName="CamelHttpMethod">
              <constant>POST</constant>
        </setHeader>

         <to uri="https://api.twilio.com/2010-04-01/Accounts/{{sms.accountSid}}/SMS/Messages" />
    </route>

</camelContext>

<!-- BEANS -->

 <bean id="httpAuth" class="org.apache.camel.component.http.HttpConfiguration">
    <property name="authMethod" value="Basic"/>
    <property name="authUsername" value="${sms.accountSid}"/>
    <property name="authPassword" value="${sms.authToken}"/>
</bean>

<bean id="http" class="org.apache.camel.component.http.HttpComponent">
    <property name="camelContext" ref="jellyfish-messaging-sms"/>
    <property name="httpConfiguration" ref="httpAuth"/>
</bean>

Here's the exception (from karaf.log):-

2013-09-09 17:37:27,627 | INFO  | umer[sms.twilio] | HttpMethodDirector               | 366 - org.apache.servicemix.bundles.commons-httpclient - 3.1.0.7 | No credentials available for BASIC 'Twilio API'@api.twilio.com:443
2013-09-09 17:37:27,632 | ERROR | umer[sms.twilio] | jellyfish-messaging              | 266 - org.apache.camel.camel-core - 2.10.2 | Dead letter interceptor invoked
2013-09-09 17:37:27,635 | ERROR | umer[sms.twilio] | sms                              | 266 - org.apache.camel.camel-core - 2.10.2 | Exchange[ExchangePattern:InOnly, BodyType:String, Body:Nothing to see here, CaughtExceptionType:org.apache.camel.component.http.HttpOperationFailedException, CaughtExceptionMessage:HTTP operation failed invoking https://api.twilio.com/2010-04-01/Accounts/...snip.../SMS/Messages?From=%2B44...snip...&To=%2B44...snip...&Body=fred with statusCode: 401, StackTrace:org.apache.camel.component.http.HttpOperationFailedException: HTTP operation failed invoking https://api.twilio.com/2010-04-01/Accounts/...snip.../SMS/Messages?From=%2B44...snip...&To=%2B44...snip...&Body=fred with statusCode: 401   at org.apache.camel.component.http.HttpProducer.populateHttpOperationFailedException(HttpProducer.java:229) at org.apache.camel.component.http.HttpProducer.process(HttpProducer.java:157)

And here's the working curl:-

curl -X POST https://api.twilio.com/2010-04-01/Accounts/...snip.../SMS/Messages -u ...snip...:...snip... -d "From=+44...snip..." -d "To=+44...snip..." -d 'Body=Hello'
4

2 回答 2

2

So, I've found a way around this, with a bit of help from a friend, Eclipse debugging and the Charles proxy. For some reason, the HttpConfiguration and HttpComponent objects were not being correctly attached to the Camel context, meaning that Camel was generating another HTTP configuration, which was unaware of the auth details.

The solution bypasses the bean-based solution for authentication, using instead the authentication options on the endpoint. It also sets the message body to be the query parameters (which I build in a bean from the contents of the message body), rather than use the *Exchange.HTTP_QUERY* JMS header (aka CamelHttpQuery).

<route id="sms.twilio">
  <from uri="activemq:sms.twilio" />
    <unmarshal ref="smsProto" />

    <!-- set the query params to be sent -->
    <transform>
      <method bean="Sender" method="getQuery"/>
    </transform>

    <to uri="activemq:sms.twilio?preserveMessageQos=true" />
</route>

<route id="sms.twilio.send">
  <from uri="activemq:sms.twilio.send" />

  <setHeader headerName="Content-Type">
    <constant>application/x-www-form-urlencoded</constant>
  </setHeader>
  <setHeader headerName="CamelHttpMethod">
    <constant>POST</constant>
  </setHeader>

 <to uri="https://api.twilio.com/2010-04-01/Accounts/{{sms.accountSid}}/Messages?authMethod=Basic&amp;authUsername={{sms.accountSid}}&amp;authPassword={{sms.authToken}}" />
</route>

I would be interested to know why the bean-based authentication didn't work, however.

J.

于 2013-09-20T17:05:18.297 回答
2

I was experiencing a similar situation, my .to(http://) endpoint ( (with a Bean configuration similar to yours) would suddenly throw HTTP 403 Forbidden responses, while it used to work correctly and still worked correctly when using curl.

Here, the trick was to use Preemptive Authentication. Even if you properly configure authUsername, authPassword and authMethod on your Camel endpoint (through beans or in the endpoint itself), Camel does not send the authentication details with the first request. It expects a 401 Unauthorized before it sends along the authentication details in a new request.

I only found this out by using tcpdump to inspect the requests, but the solution is simple:

.to("http://endpoint" + "?httpClient.authenticationPreemptive=true")

Source: http://camel.465427.n5.nabble.com/HTTP-Basic-Authentication-tp5742229p5742347.html Documentation: http://camel.apache.org/http.html

于 2017-04-12T07:28:58.953 回答