I am new to Volley and OkHttp and I am trying to login to a website using OkHttp as the transport layer for Volley. Login needs to be done through SSL and has several redirects implemented.
OkHttp has been forced to not automatically follow redirects as I need to do this manually for this site (I think), but I am failing at the post request (I am not sure though).
The post requests needs some params provided like this:
source=AEM&target=IAM&URL=selfcare&login-form-type=pwd&username=user&userId=user&password=pass&rememberUsername=yes&button=
So it should be a StringRequest if I am not mistaken, furthermore I need to override the headers for all the requests like this:
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Connection", "keep-alive");
params.put("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36");
params.put("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
return params;
}
I would be gratefull if someone could overlook my code, to see where I am going wrong. Once again, I am new to volley and okhttp, so downvoters pls be gentle.
Edit:
For anyone who has the same problem.
I finally managed to get a successfull login with the help of amoung the PersistentCookieStore (https://gist.github.com/franmontiel/ed12a2295566b7076161).
Also I needed to make a synchronized RequestQueue to be able to follow all the redirects manually. (this could be improved by using FutureRequest but it works, feel free to change the below working code to your needs.
Singleton class:
public class TestController extends Application {
public static final String TAG = "VolleyPatterns";
private RequestQueue mRequestQueue;
private static TestController sInstance;
@Override
public void onCreate() {
super.onCreate();
sInstance = this;
}
public static synchronized TestController getInstance() {
return sInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext(), new OkHttpStack());
}
return mRequestQueue;
}
public <T> void addToRequestQueue(Request<T> req, String tag) {
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
VolleyLog.e("Adding request to queue: %s", req.getUrl());
getRequestQueue().add(req);
}
public <T> void addToRequestQueue(Request<T> req) {
req.setTag(TAG);
getRequestQueue().add(req);
}
public void cancelPendingRequests(Object tag) {
if (mRequestQueue != null) {
mRequestQueue.cancelAll(tag);
}
}
}
OkHttpStack class (SSL allow all certs for testing purposes):
public class OkHttpStack extends HurlStack {
private final OkUrlFactory okUrlFactory;
public OkHttpStack() {
this(new OkUrlFactory(getOkHttpClient()));
}
public OkHttpStack(OkUrlFactory okUrlFactory) {
if (okUrlFactory == null) {
throw new NullPointerException("Client must not be null.");
}
this.okUrlFactory = okUrlFactory;
}
private static OkHttpClient getOkHttpClient() {
if (BuildConfig.DEBUG) {
return getUnsafeOkHttpClient();
} else {
return new OkHttpClient();
}
}
private static OkHttpClient getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setFollowRedirects(false);
okHttpClient.setSslSocketFactory(sslSocketFactory);
okHttpClient.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return okHttpClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
protected HttpURLConnection createConnection(URL url) throws IOException {
return okUrlFactory.open(url);
}
}
MainActivity:
public class MainActivity extends AppCompatActivity {
private String LOGINURL = "https://www.test.com/testhandler/iam/authenticateuser/";
private static String redirectURL;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CookieManager cmrCookieMan = new CookieManager(new PersistentCookieStore(this), CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(cmrCookieMan);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
final StringRequest postRequest = new StringRequest(Request.Method.POST, LOGINURL,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("Response", response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
redirectURL = null;
final int status = error.networkResponse.statusCode;
if (HttpURLConnection.HTTP_MOVED_PERM == status || status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_SEE_OTHER) {
redirectURL = error.networkResponse.headers.get("Location");
Log.e("Redirected to ", redirectURL);
Log.d("ERROR", "error => " + error.toString());
LoadFirst();
Log.w("1st ", "Link");
}
}
}
) {
@Override
public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=utf-8";
}
// this is the relevant method
@Override
public byte[] getBody() throws AuthFailureError {
String httpPostBody ="username=yser&password=pass";
try {
httpPostBody = httpPostBody + URLEncoder.encode("", "UTF-8");
} catch (UnsupportedEncodingException exception) {
Log.e("ERROR", "exception", exception);
return null;
}
return httpPostBody.getBytes();
}
};
TestController.getInstance().addToRequestQueue(postRequest);
}
private void LoadFirst() {
StringRequest stringRequest = new StringRequest(Request.Method.GET, redirectURL,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.e("Response:", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
redirectURL = null;
final int status = error.networkResponse.statusCode;
if (HttpURLConnection.HTTP_MOVED_PERM == status || status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_SEE_OTHER) {
redirectURL = error.networkResponse.headers.get("Location");
Log.e("2nd Redirect to ", redirectURL);
LoadSecond();
Log.w("2nd ", "Link");
}
}
});
TestController.getInstance().addToRequestQueue(stringRequest);
}
private void LoadSecond() {
StringRequest stringRequest = new StringRequest(Request.Method.GET, redirectURL,
new Response.Listener<String>() {
@Override
public void onResponse(String s) {
//Search the secretUserID in the response, which is only provided after successfull login
for (String line : s.split("\n")) {
if (line.contains("secretUserID")) {
String[] out = line.split("'");
String CustID = out[1].toString();
Log.w("secretUserID", CustID);
}
}
Log.w("3rd FINAL ", "Successfully logged in!");
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("Fatal error: ", error.toString());
if (error instanceof TimeoutError) {
// note : may cause recursive invoke if always timeout.
Log.e("Timed out ", "retrying..");
LoadSecond();
}
}
});
stringRequest.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS * 2,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
TestController.getInstance().addToRequestQueue(stringRequest);
}
}
Logcat:
03-23 16:19:46.791 5847-5878/test.com.example.buddy.myapplication D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
03-23 16:19:46.846 5847-5878/test.com.example.buddy.myapplication I/OpenGLRenderer: Initialized EGL, version 1.4
03-23 16:19:47.534 5847-5877/test.com.example.buddy.myapplication E/Volley: [1644] BasicNetwork.performRequest: Request at https://www.test.com/testhandler/iam/authenticateuser/ has been redirected to https://www.test.com/login.login/?url=%2Ftesthandler%2Fiam%2Fauthenticateuser%2F
03-23 16:19:47.538 5847-5847/test.com.example.buddy.myapplication E/Redirected to: https://www.test.com/login.login/?url=%2Fztesthandler%2Fiam%2Fauthenticateuser%2F
03-23 16:19:47.538 5847-5847/test.com.example.buddy.myapplication D/ERROR: error => com.android.volley.RedirectError
03-23 16:19:47.540 5847-5847/test.com.example.buddy.myapplication W/1st: Link
03-23 16:19:47.611 5847-5874/test.com.example.buddy.myapplication E/Volley: [1641] BasicNetwork.performRequest: Request at https://www.test.com/login.login/?url=%2Ftesthandler%2Fiam%2Fauthenticateuser%2F has been redirected to https://www.test.com/test/?selfcare
03-23 16:19:47.612 5847-5847/test.com.example.buddy.myapplication E/2nd Redirect to: https://www.test.com/test/?selfcare
03-23 16:19:47.612 5847-5847/test.com.example.buddy.myapplication W/2nd: Link
03-23 16:19:51.533 5847-5875/test.com.example.buddy.myapplication D/Volley: [1642] BasicNetwork.logSlowRequests: HTTP response for request=<[ ] https://www.test.com/test/?selfcare 0xe5b1fae3 NORMAL 3> [lifetime=3920], [size=34928], [rc=200], [retryCount=0]
03-23 16:19:51.544 5847-5847/test.com.example.buddy.myapplication W/encryptedCustID: someUserID
03-23 16:19:51.545 5847-5847/test.com.example.buddy.myapplication W/3rd FINAL: Successfully logged in!