Skip to content

Commit 80d0597

Browse files
committed
Add support for BasicAuth + alwaysSendBasicAuthHeaders in JsonServiceClient
1 parent 7c60dcd commit 80d0597

File tree

6 files changed

+1776
-1430
lines changed

6 files changed

+1776
-1430
lines changed

src/AndroidClient/client/src/main/java/net/servicestack/client/HttpHeaders.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
public class HttpHeaders {
77
public static final String Accept = "Accept";
8-
public static final String ContentType = "Content-Type";
8+
public static final String Authorization = "Authorization";
99
public static final String ContentLength = "Content-Length";
10+
public static final String ContentType = "Content-Type";
1011
}

src/AndroidClient/client/src/main/java/net/servicestack/client/JsonServiceClient.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ public class JsonServiceClient implements ServiceClient {
3333
String baseUrl;
3434
String replyUrl;
3535

36+
boolean alwaysSendBasicAuthHeaders;
37+
String userName;
38+
String password;
39+
3640
Integer timeoutMs;
3741
public ConnectionFilter RequestFilter;
3842
public ConnectionFilter ResponseFilter;
@@ -161,6 +165,10 @@ public HttpURLConnection createRequest(String requestUrl, String httpMethod, byt
161165
req.setRequestProperty(HttpHeaders.ContentType, requestType);
162166
}
163167

168+
if (alwaysSendBasicAuthHeaders) {
169+
addBasicAuth(req, userName, password);
170+
}
171+
164172
if (RequestFilter != null) {
165173
RequestFilter.exec(req);
166174
}
@@ -185,6 +193,11 @@ public HttpURLConnection createRequest(String requestUrl, String httpMethod, byt
185193
}
186194
}
187195

196+
private static void addBasicAuth(HttpURLConnection req, String userName, String password) {
197+
req.setRequestProperty(HttpHeaders.Authorization,
198+
"Basic " + Utils.toBase64String(userName + ":" + password));
199+
}
200+
188201
public static RuntimeException createException(HttpURLConnection res, int responseCode){
189202

190203
WebServiceException webEx = null;
@@ -259,6 +272,17 @@ public HttpURLConnection createSendRequest(Object request) {
259272
}
260273
}
261274

275+
@Override
276+
public void setAlwaysSendBasicAuthHeaders(boolean value) {
277+
this.alwaysSendBasicAuthHeaders = value;
278+
}
279+
280+
@Override
281+
public void setCredentials(String userName, String password) {
282+
this.userName = userName;
283+
this.password = password;
284+
}
285+
262286
@Override
263287
public <TResponse> TResponse send(IReturn<TResponse> request) {
264288
return send(

src/AndroidClient/client/src/main/java/net/servicestack/client/ServiceClient.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import java.util.Map;
99

1010
public interface ServiceClient {
11+
void setAlwaysSendBasicAuthHeaders(boolean value);
12+
void setCredentials(String userName, String password);
13+
1114
<TResponse> TResponse send(IReturn<TResponse> request);
1215
void send(IReturnVoid request);
1316

src/AndroidClient/client/src/main/java/net/servicestack/client/Utils.java

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ public static boolean matchesContentType(String contentType, String matchesConte
529529
}
530530

531531
public static String sanitizeVarName(String name){
532-
return name.replaceAll("_","").toLowerCase();
532+
return name.replaceAll("_", "").toLowerCase();
533533
}
534534

535535
public static ResponseStatus createResponseStatus(Object obj) {
@@ -671,4 +671,115 @@ public static <K,V> HashMap<K,ArrayList<V>> createMap(ArrayList<V> xs, Function<
671671

672672
return to;
673673
}
674+
675+
//From: http://iharder.sourceforge.net/current/java/base64/ (Public Domain)
676+
public static String toBase64String(String source) {
677+
return toBase64String(toUtf8Bytes(source));
678+
}
679+
680+
public static String toBase64String(byte[] source) {
681+
byte[] encoded = toBase64Bytes(source);
682+
try {
683+
return new String(encoded, "US-ASCII");
684+
} catch (UnsupportedEncodingException e) {
685+
return new String(encoded);
686+
}
687+
}
688+
689+
public static byte[] toBase64Bytes(byte[] source) {
690+
return toBase64Bytes(source, 0, source.length);
691+
}
692+
693+
public static byte[] toBase64Bytes(byte[] source, int off, int len) {
694+
if (source == null)
695+
throw new NullPointerException("Cannot serialize a null array.");
696+
697+
if (off < 0)
698+
throw new IllegalArgumentException("Cannot have negative offset: " + off);
699+
700+
if (len < 0)
701+
throw new IllegalArgumentException("Cannot have length offset: " + len);
702+
703+
if (off + len > source.length)
704+
throw new IllegalArgumentException(String.format(
705+
"Cannot have offset of %d and length of %d with array of length %d", off, len, source.length));
706+
707+
int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0); // Bytes needed for actual encoding
708+
byte[] outBuff = new byte[encLen];
709+
710+
int d = 0;
711+
int e = 0;
712+
int len2 = len - 2;
713+
for (; d < len2; d += 3, e += 4) {
714+
encode3to4(source, d + off, 3, outBuff, e);
715+
}
716+
717+
if (d < len) {
718+
encode3to4(source, d + off, len - d, outBuff, e);
719+
e += 4;
720+
}
721+
722+
// Only resize array if we didn't guess it right.
723+
if (e <= outBuff.length - 1) {
724+
byte[] finalOut = new byte[e];
725+
System.arraycopy(outBuff, 0, finalOut, 0, e);
726+
return finalOut;
727+
} else {
728+
return outBuff;
729+
}
730+
}
731+
732+
private final static byte EQUALS_SIGN = (byte)'=';
733+
734+
private final static byte[] _STANDARD_ALPHABET = {
735+
(byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
736+
(byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
737+
(byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
738+
(byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
739+
(byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
740+
(byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
741+
(byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
742+
(byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
743+
(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
744+
(byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
745+
};
746+
747+
private static byte[] encode3to4(
748+
byte[] source, int srcOffset, int numSigBytes,
749+
byte[] destination, int destOffset) {
750+
751+
byte[] ALPHABET = _STANDARD_ALPHABET;
752+
753+
int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 )
754+
| ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
755+
| ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
756+
757+
switch (numSigBytes)
758+
{
759+
case 3:
760+
destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
761+
destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
762+
destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
763+
destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ];
764+
return destination;
765+
766+
case 2:
767+
destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
768+
destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
769+
destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
770+
destination[ destOffset + 3 ] = EQUALS_SIGN;
771+
return destination;
772+
773+
case 1:
774+
destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
775+
destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
776+
destination[ destOffset + 2 ] = EQUALS_SIGN;
777+
destination[ destOffset + 3 ] = EQUALS_SIGN;
778+
return destination;
779+
780+
default:
781+
return destination;
782+
}
783+
}
784+
674785
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package net.servicestack.client.tests;
2+
3+
import junit.framework.TestCase;
4+
5+
import net.servicestack.client.JsonServiceClient;
6+
import net.servicestack.client.WebServiceException;
7+
8+
import net.servicestack.client.tests.testdtos.*;
9+
10+
/**
11+
* Created by mythz on 1/3/2016.
12+
*/
13+
public class TestAuthTests extends TestCase {
14+
15+
JsonServiceClient client = new JsonServiceClient("http://test.servicestack.net");
16+
17+
public void test_AuthRequired_returns_401(){
18+
try {
19+
client.get(new TestAuth());
20+
fail("should throw");
21+
} catch (WebServiceException ex){
22+
assertEquals(401, ex.getStatusCode());
23+
assertEquals("Unauthorized", ex.getStatusDescription());
24+
}
25+
}
26+
27+
public void test_does_send_BasicAuthHeaders(){
28+
client.setCredentials("test", "test");
29+
client.setAlwaysSendBasicAuthHeaders(true);
30+
31+
TestAuthResponse response = client.get(new TestAuth());
32+
33+
assertEquals("1", response.getUserId());
34+
assertEquals("test", response.getUserName());
35+
assertEquals("test DisplayName", response.getDisplayName());
36+
assertNotNull(response.getSessionId());
37+
}
38+
}

0 commit comments

Comments
 (0)