Browse Source

add support for HTTP basic authentication (#79)

This changeset adds a simple request interceptor that performs HTTP basic authentication.

The HTTP spec isn't very clear on the use of character encodings within this header.
The most common interpretation in servers appears to be to expect ISO-8859-1, so I've
used that as a default, as well as allowing the encoding to be specified.

At @adriancole's suggestion, sun.misc.BASE64Encoder is used for the base64 encoding
rather than pulling an implementation into the Util class.  If we ever run into a JRE that
doesn't provide compatibility with that class, we can eliminate that dependency.
pull/89/head
David M. Carr 11 years ago committed by adriancole
parent
commit
42a74cfaed
  1. 3
      CHANGES.md
  2. 8
      README.md
  3. 4
      core/src/main/java/feign/Util.java
  4. 69
      core/src/main/java/feign/auth/BasicAuthRequestInterceptor.java
  5. 41
      core/src/test/java/feign/auth/BasicAuthRequestInterceptorTest.java

3
CHANGES.md

@ -1,3 +1,6 @@ @@ -1,3 +1,6 @@
### Version 5.4.0
* Add `BasicAuthRequestInterceptor`
### Version 5.3.0
* Split `GsonCodec` into `GsonEncoder` and `GsonDecoder`, which are easy to use with `Feign.Builder`
* Deprecate `GsonCodec`

8
README.md

@ -56,7 +56,7 @@ For further flexibility, you can use Dagger modules directly. See the `Dagger` @@ -56,7 +56,7 @@ For further flexibility, you can use Dagger modules directly. See the `Dagger`
When you need to change all requests, regardless of their target, you'll want to configure a `RequestInterceptor`.
For example, if you are acting as an intermediary, you might want to propagate the `X-Forwarded-For` header.
```
```java
static class ForwardedForInterceptor implements RequestInterceptor {
@Override public void apply(RequestTemplate template) {
template.header("X-Forwarded-For", "origin.host.com");
@ -66,6 +66,12 @@ static class ForwardedForInterceptor implements RequestInterceptor { @@ -66,6 +66,12 @@ static class ForwardedForInterceptor implements RequestInterceptor {
Bank bank = Feign.builder().decoder(accountDecoder).requestInterceptor(new ForwardedForInterceptor()).target(Bank.class, "https://api.examplebank.com");
```
Another common example of an interceptor would be authentication, such as using the built-in `BasicAuthRequestInterceptor`.
```java
Bank bank = Feign.builder().decoder(accountDecoder).requestInterceptor(new BasicAuthRequestInterceptor(username, password)).target(Bank.class, "https://api.examplebank.com");
```
### Multiple Interfaces
Feign can produce multiple api interfaces. These are defined as `Target<T>` (default `HardCodedTarget<T>`), which allow for dynamic discovery and decoration of requests prior to execution.

4
core/src/main/java/feign/Util.java

@ -60,6 +60,10 @@ public class Util { @@ -60,6 +60,10 @@ public class Util {
* UTF-8: eight-bit UCS Transformation Format.
*/
public static final Charset UTF_8 = Charset.forName("UTF-8");
/**
* ISO-8859-1: ISO Latin Alphabet Number 1 (ISO-LATIN-1).
*/
public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
/**
* Copy of {@code com.google.common.base.Preconditions#checkArgument}.

69
core/src/main/java/feign/auth/BasicAuthRequestInterceptor.java

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
/*
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package feign.auth;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import sun.misc.BASE64Encoder;
import java.nio.charset.Charset;
import static feign.Util.checkNotNull;
import static feign.Util.ISO_8859_1;
/**
* An interceptor that adds the request header needed to use HTTP basic authentication.
*/
public class BasicAuthRequestInterceptor implements RequestInterceptor {
private final String headerValue;
/**
* Creates an interceptor that authenticates all requests with the specified username and password encoded using
* ISO-8859-1.
*
* @param username the username to use for authentication
* @param password the password to use for authentication
*/
public BasicAuthRequestInterceptor(String username, String password) {
this(username, password, ISO_8859_1);
}
/**
* Creates an interceptor that authenticates all requests with the specified username and password encoded using
* the specified charset.
*
* @param username the username to use for authentication
* @param password the password to use for authentication
* @param charset the charset to use when encoding the credentials
*/
public BasicAuthRequestInterceptor(String username, String password, Charset charset) {
checkNotNull(username, "username");
checkNotNull(password, "password");
this.headerValue = "Basic " + base64Encode((username + ":" + password).getBytes(charset));
}
@Override public void apply(RequestTemplate template) {
template.header("Authorization", headerValue);
}
/*
* This uses a Sun internal method; if we ever encounter a case where this method is not available, the appropriate
* response would be to pull the necessary portions of Guava's BaseEncoding class into Util.
*/
private static String base64Encode(byte[] bytes) {
return new BASE64Encoder().encode(bytes);
}
}

41
core/src/test/java/feign/auth/BasicAuthRequestInterceptorTest.java

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
/*
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package feign.auth;
import feign.RequestTemplate;
import org.testng.annotations.Test;
import java.util.Collection;
import java.util.Collections;
import static org.testng.Assert.assertEquals;
/**
* Tests for {@link BasicAuthRequestInterceptor}.
*/
public class BasicAuthRequestInterceptorTest {
/**
* Tests that request headers are added as expected.
*/
@Test public void testAuthentication() {
RequestTemplate template = new RequestTemplate();
BasicAuthRequestInterceptor interceptor = new BasicAuthRequestInterceptor("Aladdin", "open sesame");
interceptor.apply(template);
Collection<String> actualValue = template.headers().get("Authorization");
Collection<String> expectedValue = Collections.singletonList("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
assertEquals(actualValue, expectedValue);
}
}
Loading…
Cancel
Save