Skip to content

Commit 2af0bc8

Browse files
author
Noor Dawod
committed
Merge pull request android-async-http#622 from fineswap/Http401Auth
Add example for implementing HTTP 401 AUTH using the library.
2 parents 56ad69a + 35625ff commit 2af0bc8

File tree

8 files changed

+296
-2
lines changed

8 files changed

+296
-2
lines changed

sample/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
<activity android:name=".CustomCASample"/>
3737
<activity android:name=".RetryRequestSample"/>
3838
<activity android:name=".RangeResponseSample"/>
39+
<activity android:name=".Http401AuthSample"/>
3940

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

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, Http401AuthSample.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)