Skip to content

Commit 34331df

Browse files
author
Noor Dawod
committed
Add example for implementing HTTP 401 AUTH using the library.
1 parent 3903b24 commit 34331df

File tree

8 files changed

+293
-2
lines changed

8 files changed

+293
-2
lines changed

sample/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<activity android:name=".CustomCASample"/>
3636
<activity android:name=".RetryRequestSample"/>
3737
<activity android:name=".RangeResponseSample"/>
38+
<activity android:name=".Http401Auth"/>
3839

3940
<service android:name=".services.ExampleIntentService"/>
4041
</application>
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/*
2+
Android Asynchronous Http Client Sample
3+
Copyright (c) 2014 Marek Sebera <[email protected]>
4+
http://loopj.com
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
package com.loopj.android.http.sample;
20+
21+
import android.app.AlertDialog;
22+
import android.content.DialogInterface;
23+
import android.util.Log;
24+
import android.view.LayoutInflater;
25+
import android.view.View;
26+
import android.widget.EditText;
27+
import android.widget.TextView;
28+
import com.fasterxml.jackson.core.JsonFactory;
29+
import com.fasterxml.jackson.databind.ObjectMapper;
30+
31+
import com.loopj.android.http.AsyncHttpClient;
32+
import com.loopj.android.http.Base64;
33+
import com.loopj.android.http.BaseJsonHttpResponseHandler;
34+
import com.loopj.android.http.RequestHandle;
35+
import com.loopj.android.http.ResponseHandlerInterface;
36+
import com.loopj.android.http.sample.util.SampleJSON;
37+
import java.util.List;
38+
import org.apache.http.Header;
39+
import org.apache.http.HttpEntity;
40+
import org.apache.http.message.BasicHeader;
41+
42+
/**
43+
* This sample demonstrates how to implement HTTP 401 Basic Authentication.
44+
*
45+
* @author Noor Dawod <[email protected]>
46+
*/
47+
public class Http401Auth extends GetSample {
48+
49+
private static final String LOG_TAG = "Http401Auth";
50+
private static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate";
51+
private static final String HEADER_AUTHORIZATION = "Authorization";
52+
private static final String HEADER_REALM_PREFIX = "realm=";
53+
private static final String HEADER_BASIC = "basic";
54+
55+
private static final String SECRET_USERNAME = "ahc";
56+
private static final String SECRET_PASSWORD = "LetMeIn";
57+
58+
private String userName;
59+
private String passWord;
60+
61+
public void retryRequest() {
62+
// File is still smaller than remote file; send a new request.
63+
onRunButtonPressed();
64+
}
65+
66+
@Override
67+
public String getDefaultURL() {
68+
return "http://httpbin.org/basic-auth/" + SECRET_USERNAME + "/" + SECRET_PASSWORD;
69+
}
70+
71+
@Override
72+
public int getSampleTitle() {
73+
return R.string.title_401_unauth;
74+
}
75+
76+
@Override
77+
public RequestHandle executeSample(AsyncHttpClient client, String URL, Header[] headers, HttpEntity entity, ResponseHandlerInterface responseHandler) {
78+
return client.get(this, URL, headers, null, responseHandler);
79+
}
80+
81+
@Override
82+
public Header[] getRequestHeaders() {
83+
List<Header> headers = getRequestHeadersList();
84+
85+
// Add authentication header.
86+
if (userName != null && passWord != null) {
87+
byte[] base64bytes = Base64.encode(
88+
(userName + ":" + passWord).getBytes(),
89+
Base64.DEFAULT
90+
);
91+
String credentials = new String(base64bytes);
92+
headers.add(new BasicHeader(HEADER_AUTHORIZATION, HEADER_BASIC + " " + credentials));
93+
}
94+
95+
return headers.toArray(new Header[headers.size()]);
96+
}
97+
98+
@Override
99+
public ResponseHandlerInterface getResponseHandler() {
100+
return new BaseJsonHttpResponseHandler<SampleJSON>() {
101+
102+
@Override
103+
public void onStart() {
104+
clearOutputs();
105+
}
106+
107+
@Override
108+
public void onSuccess(int statusCode, Header[] headers, String rawJsonResponse, SampleJSON response) {
109+
debugHeaders(LOG_TAG, headers);
110+
debugStatusCode(LOG_TAG, statusCode);
111+
if (response != null) {
112+
debugResponse(LOG_TAG, rawJsonResponse);
113+
}
114+
}
115+
116+
@Override
117+
public void onFailure(int statusCode, Header[] headers, Throwable throwable, String rawJsonData, SampleJSON errorResponse) {
118+
debugHeaders(LOG_TAG, headers);
119+
debugStatusCode(LOG_TAG, statusCode);
120+
debugThrowable(LOG_TAG, throwable);
121+
122+
// Ask the user for credentials if required by the server.
123+
if (statusCode == 401) {
124+
String realm = "Protected Page";
125+
String authType = null;
126+
127+
// Cycle through the headers and look for the WWW-Authenticate header.
128+
for (Header header : headers) {
129+
String headerName = header.getName();
130+
if (HEADER_WWW_AUTHENTICATE.equalsIgnoreCase(headerName)) {
131+
String headerValue = header.getValue().trim();
132+
String headerValueLowerCase = headerValue.toLowerCase();
133+
134+
// Get the type of auth requested.
135+
int charPos = headerValueLowerCase.indexOf(' ');
136+
if(0 < charPos) {
137+
authType = headerValueLowerCase.substring(0, charPos);
138+
139+
// The second part should begin with a "realm=" prefix.
140+
if(headerValueLowerCase.substring(1 + charPos).startsWith(HEADER_REALM_PREFIX)) {
141+
// The new realm value, including any possible wrapping quotation.
142+
realm = headerValue.substring(1 + charPos + HEADER_REALM_PREFIX.length());
143+
144+
// If realm starts with a quote, remove surrounding quotes.
145+
if (realm.charAt(0) == '"' || realm.charAt(0) == '\'') {
146+
realm = realm.substring(1, realm.length() - 1);
147+
}
148+
}
149+
}
150+
}
151+
}
152+
153+
// We will support basic auth in this sample.
154+
if (authType != null && HEADER_BASIC.equals(authType)) {
155+
// Show a dialog for the user and request user/pass.
156+
Log.d(LOG_TAG, "realm=" + realm);
157+
158+
// Present the dialog.
159+
postRunnable(new DialogRunnable(realm));
160+
}
161+
}
162+
}
163+
164+
@Override
165+
protected SampleJSON parseResponse(String rawJsonData, boolean isFailure) throws Throwable {
166+
return new ObjectMapper().readValues(new JsonFactory().createParser(rawJsonData), SampleJSON.class).next();
167+
}
168+
};
169+
}
170+
171+
private class DialogRunnable implements Runnable, DialogInterface.OnClickListener {
172+
173+
final String realm;
174+
final View dialogView;
175+
176+
public DialogRunnable(String realm) {
177+
this.realm = realm;
178+
this.dialogView = LayoutInflater.from(Http401Auth.this).inflate(R.layout.credentials, null);
179+
180+
// Update the preface text with correct credentials.
181+
TextView preface = (TextView)dialogView.findViewById(R.id.label_credentials);
182+
String prefaceText = preface.getText().toString();
183+
184+
// Substitute placeholders, and re-set the value.
185+
preface.setText(String.format(prefaceText, SECRET_USERNAME, SECRET_PASSWORD));
186+
}
187+
188+
@Override
189+
public void run() {
190+
AlertDialog.Builder builder = new AlertDialog.Builder(Http401Auth.this);
191+
builder.setTitle(realm);
192+
builder.setView(dialogView);
193+
builder.setPositiveButton(android.R.string.ok, this);
194+
builder.setNegativeButton(android.R.string.cancel, this);
195+
builder.show();
196+
}
197+
198+
@Override
199+
public void onClick(DialogInterface dialog, int which) {
200+
switch (which) {
201+
case DialogInterface.BUTTON_POSITIVE:
202+
// Dismiss the dialog.
203+
dialog.dismiss();
204+
205+
// Update the username and password variables.
206+
userName = ((EditText) dialogView.findViewById(R.id.field_username)).getText().toString();
207+
passWord = ((EditText) dialogView.findViewById(R.id.field_password)).getText().toString();
208+
209+
// Refetch the remote file.
210+
retryRequest();
211+
212+
break;
213+
214+
case DialogInterface.BUTTON_NEGATIVE:
215+
// Dismiss the dialog.
216+
dialog.dismiss();
217+
218+
break;
219+
}
220+
}
221+
}
222+
}

sample/src/main/java/com/loopj/android/http/sample/WaypointsActivity.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public class WaypointsActivity extends ListActivity {
5050
new SampleConfig(R.string.title_persistent_cookies, PersistentCookiesSample.class),
5151
new SampleConfig(R.string.title_custom_ca, CustomCASample.class),
5252
new SampleConfig(R.string.title_retry_handler, RetryRequestSample.class),
53-
new SampleConfig(R.string.title_range_sample, RangeResponseSample.class)
53+
new SampleConfig(R.string.title_range_sample, RangeResponseSample.class),
54+
new SampleConfig(R.string.title_401_unauth, Http401Auth.class)
5455
};
5556

5657
@Override
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<LinearLayout
4+
xmlns:android="http://schemas.android.com/apk/res/android"
5+
android:layout_width="fill_parent"
6+
android:layout_height="wrap_content"
7+
android:paddingTop="10dp"
8+
android:paddingBottom="0dp"
9+
android:paddingLeft="10dp"
10+
android:paddingRight="10dp"
11+
android:orientation="vertical"
12+
>
13+
<TextView
14+
android:id="@+id/label_credentials"
15+
android:layout_width="fill_parent"
16+
android:layout_height="wrap_content"
17+
android:layout_marginTop="0dp"
18+
android:layout_marginBottom="10dp"
19+
android:layout_marginLeft="0dp"
20+
android:layout_marginRight="0dp"
21+
android:text="@string/label_credentials"
22+
android:textColor="@color/dialog_color"
23+
/>
24+
<EditText
25+
android:id="@+id/field_username"
26+
android:layout_width="fill_parent"
27+
android:layout_height="wrap_content"
28+
android:layout_marginTop="0dp"
29+
android:layout_marginBottom="5dp"
30+
android:layout_marginLeft="0dp"
31+
android:layout_marginRight="0dp"
32+
android:inputType="text"
33+
android:hint="@string/field_username"
34+
/>
35+
<EditText
36+
android:id="@+id/field_password"
37+
android:layout_width="fill_parent"
38+
android:layout_height="wrap_content"
39+
android:layout_marginTop="0dp"
40+
android:layout_marginBottom="5dp"
41+
android:layout_marginLeft="0dp"
42+
android:layout_marginRight="0dp"
43+
android:inputType="textPassword"
44+
android:hint="@string/field_password"
45+
/>
46+
</LinearLayout>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<resources>
2+
3+
<color name="dialog_color">#FF333333</color>
4+
5+
</resources>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<resources>
2+
3+
<color name="dialog_color">#FF333333</color>
4+
5+
</resources>

sample/src/main/res/values/colors.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<resources>
2+
3+
<color name="dialog_color">#FFFFFFFF</color>
4+
5+
</resources>

sample/src/main/res/values/strings.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33
<resources>
44
<string name="app_name">Android Async Http Samples</string>
55

6+
<string name="field_username">Username</string>
7+
<string name="field_password">Password</string>
8+
69
<string name="button_run">Run</string>
10+
<string name="button_cancel">Cancel</string>
11+
712
<string name="label_headers">Headers (key=val, one per line)</string>
813
<string name="label_req_body">Request body</string>
9-
<string name="button_cancel">Cancel</string>
14+
<string name="label_credentials">Server requests authentication.\nTo gain access, enter:\n\nUsername: %1$s\nPassword: %2$s</string>
1015

1116
<string name="title_get_sample">GET</string>
1217
<string name="title_json_sample">GET JSON and parse it</string>
@@ -28,4 +33,5 @@
2833
<string name="title_custom_ca">Custom CA Example</string>
2934
<string name="title_retry_handler">Retrying requests by Exception</string>
3035
<string name="title_range_sample">Range response handling</string>
36+
<string name="title_401_unauth">401 basic authentication</string>
3137
</resources>

0 commit comments

Comments
 (0)