Login läuft nicht

D

DevelopingPhil

Neues Mitglied
1
Guten Tag zusammen,

ich habe ein etwas komisches Problem. Und zwar möchte ich einen sicheren Login bauen aber es klappt überhaupt nicht. Eigentlich müsste mir der Server bei richtiger Dateneingabe auch eine anständige Antwort geben. Stattdesssen kommt beim Emulator die ganze Zeit Sachen wie "Skipped 33803 Frames!..." und so weiter.

Kann mir da jemand weiterhelfen?

Code:
package com.example.test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.security.NoSuchAlgorithmException;

import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.os.StrictMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;

public class LoginActivity extends ActionBarActivity {
	
	Button button;
	EditText usernameText;
	EditText passwordText;
	CheckBox savelogin;
	TextView logininfo;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_login);
		if (android.os.Build.VERSION.SDK_INT > 9) {
		    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
		    StrictMode.setThreadPolicy(policy);
		}
		ActionBar actionBar = getSupportActionBar();
        actionBar.hide();
        button = (Button) findViewById(R.id.loginButton);
        usernameText = (EditText) findViewById(R.id.usernameText);
        passwordText = (EditText) findViewById(R.id.passwordText);
		savelogin = (CheckBox) findViewById(R.id.saveloginCheckbox);
		logininfo = (TextView) findViewById(R.id.loginInfo);
        
		button.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				
				// Login ausführen
				String usernameString = usernameText.getText().toString();
				String passwordString = passwordText.getText().toString();
				
				if(usernameString != "" && passwordString != "") {
					// POST Request absetzen
					try {
						EncryptPassword ep = new EncryptPassword();
						String encryptedPassword = null;
						try {
							encryptedPassword = ep.SHA512(passwordString);
						} catch (NoSuchAlgorithmException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						String data = URLEncoder.encode("username", "UTF-8") + "=" + URLEncoder.encode(usernameString, "UTF-8");
						data += "&" + URLEncoder.encode("password", "UTF-8") + "=" + URLEncoder.encode(encryptedPassword, "UTF-8");
						logininfo.setText(data);
						String text = "";
			            
						BufferedReader reader = null;
						URL url = new URL("http://www.xyz.de/login");
						URLConnection connection = url.openConnection();
						connection.setDoOutput(true);
						OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
						writer.write(data);
						writer.flush();
						
						reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
						StringBuilder sb = new StringBuilder();
				        String line = null;
				        
				        while((line = reader.readLine()) != null) {
				        	sb.append(line + "\n");
				        }
				        
						text = sb.toString();
				        if(text == "{\"error\":\"Zu viele Anfragen.\"}") {
				        	logininfo.setText("Bitte probiere es in einer Minute noch einmal.");
				        }
						logininfo.setText(text);

					} catch (MalformedURLException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				} else {
					// nichts machen, da nicht beide Felder ausgefüllt sind
				}

			}
		});
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.login, menu);
		return true;
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
}

Mit freundlichen Grüßen
Philipp Escher
 
Logcat? NetworkOnUiThread-Exception?

lg. Dagobert
 
Die LogCat sagt immer das, was ich oben geschrieben habe. (Skipped 33803 Frames!) Was kann ich ändern, weil sobald ich Username und Passwort eingegeben habe, reagiert die App nicht mehr und es kommt auch kein Error.
 
Du führst Netzwerkzugriffe direkt im MainThread aus, das ist seit HoneyComb nicht mehr gewollt.

-> Netzwerk und andere Zeitintensive Prozesse welche das UI ausbremsen, müssen in einen Thread ausgelagert werden.

-> Googlehilfe: AsyncTask
 
Sehr richtig,

darauf wollte ich hinaus :D

lg. Dagobert
 
Code:
package com.example.app;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.StrictMode;


@SuppressLint("NewApi") public class Request  {

	@TargetApi(Build.VERSION_CODES.GINGERBREAD) public static String request(String url, String[] parameterTitles, String[] parameterValues) throws IOException {
		
		if(android.os.Build.VERSION.SDK_INT > 9) {
			StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
			StrictMode.setThreadPolicy(policy);
		}
		
		if(parameterTitles.length == parameterValues.length) {
			// Parameter Titel Anzahl stimmt mit Anzahl der Parameter Werte überein
			String parameter = null;
			String response = null;
			for(int i=0; i < parameterValues.length; i++) {
				parameter += URLEncoder.encode(parameterTitles[i], "UTF-8") + "=" + URLEncoder.encode(parameterValues[i], "UTF-8");
			}
			BufferedReader reader = null;
			URL api = new URL(url);
			URLConnection connection = api.openConnection();
			connection.setDoOutput(true);
			OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
			writer.write(parameter);
			writer.flush();
			reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			StringBuilder stringbuilder = new StringBuilder();
			String line = null;
			while((line = reader.readLine()) != null) {
				stringbuilder.append(line + "\n");
			}
			response = stringbuilder.toString();
			return response;
		} else {
			return null;
		}
	}
	
}

Habe mir jetzt extra eine Klasse für Request gebaut :thumbsup: Leider läuft die nicht :( Sieht jemand von euch den Fehler?


Aufgerufen habe ich die dann in der eigentlichen Acitivity mit

String[] parameterTitles = {"username", "password"};
String[] parameterValues = {usernameString, encryptedPassword};
String response = Request.request("...", parameterTitles, parameterValues);
 
Zuletzt bearbeitet:
also vorweg mal ein Wort zum Stil dieser Lösung: Es hat einen Grund warum keine Internet Verbindungen im Main Thread laufen sollen. Die ganze UI hängt solange bis alles abgearbeitet ist und das kann je nach Internetverbindung und Server unangenehm für den User sein.

Jetzt zu deinem Problem. Leider haben wir immernoch keine brauchbare Fehlermeldung. Falls im Logcat wirklich nichts zu einer Exception steht, dann musst du ein bisschen debuggen. Mach dir mal mit Log.d() ein paar Ausgaben in den Quelltext, damit du herausfinden kannst, wo genau der Fehler auftritt, oder setzt dir einen Breakpoint an den Anfang und geh den Ablauf dann Schrittweise durch.
 
K.a. ;) für mich ist der Code ein sorry "Misthaufen".

"läuft nicht" kann jetzt (halluziniert) folgendes bedeuten

- Encoding Problem
- Host not found
- 404
- Content-Type fehlt
- Content-Length fehlt

Da du dich um Exceptions leider nicht scherst, findest es so auch nicht raus.

Lösung: Ordentlich coden oder dir eine Library besorgen damit du nur noch Parameter einsetzen musst.
 
Habt ihr denn mal einen Beispielcode, wo Internetsachen nicht im Mainthread laufen? Ich verstehe garnicht, wie man sowas baut.

Ja der Server ist nicht erreichbar, hab diese android.os.networkexception da... Aber er ist eigentlich erreichbar, ich mache nur was in der App falsch leider und weiß nicht was :(
 
Guggst du hier: Android: Why, When and How to use AsyncTask with example | COMPILETIMEERROR.com

"... aber ... eigentlich" :) Das ist der Klassiker ;)

Es kann auch ein kaputtes / abgelaufenes SSL Zertifikat sein.

Denk mal an den Endbenutzer ;) Du scherst dich nicht um Exceptions - der
Endbenutzer startet die App. Die geht auf und sofort wieder zu.

Yeah! Solche Apps fliegen nach 5 Sekunden wieder runter...
 
ich hatte im Forum schon mal meinen Ansatz für einen Dateidownload hier gepostet. Und hier ein Beispiel für eine Verbindung zu einem Server um DB Abfragen zu machen.
 
Vielen Dank, schaue ich mir heute mal an.
 
Sooo, ich brauche doch nochmal Hilfe :(

Ich möchte jetzt die eingegebenen Daten als Parameter an den Server und das Ganze mit AsyncTask, aber ich kriege das nicht hin. Habe bei der Request Klasse "extends AsyncTask <String>" ausgewählt aber es klappt überhaupt nicht. Hat jemand einen kurzen knackigen Code, anhand ich dem verstehen kann?
 
"knackiger Code" ... Dachte eigentlich den Async Task aus meinem DB Beispiel könnte man für einen Login über HTTP fast ohne Anpassungen übernehmen.
 
Ich steige bei deinem Code nicht ganz durch...

Ich möchte in meiner LoginActivity.java bei onclick auf einen Button einfach die eingegebenen Daten an eine URL schicken und das Ergebnis auswerten. Wollte jetzt eigentlich eine Klasse bauen, an die ich jede URL und Parameter übergeben kann :thumbsup: Diese Klasse müsste den Request einfach ausführen und den Ergebnisstring per return zurückgeben. Verstehst du, was ich meine?
 
Ja und wo ist da problem?

Code:
public NetworkBackgroundTask(final String url, final ResponseHandler handler) {
        this(url, null, handler);
        this.method = GET;
    }

    public NetworkBackgroundTask(final String url, final String outputJson, final ResponseHandler handler) {
        this.url = url;
        this.outputJson = outputJson;
        this.handler = handler;
        this.timeout = DEFAULT_TIMEOUT;
        this.method = POST;
    }

    public void setCredentials(final long userId, final String userToken) {
        this.userId = String.valueOf(userId);
        this.userToken = userToken;
    }

    @Override
    protected Void doInBackground(Void... params) {
        String result = null;
        int httpStatus;
        HttpsURLConnection con = null;
        Log.i(TAG, "URL: " + url);
        Log.i(TAG, "Method: " + method);
        try {
            con = (HttpsURLConnection) new URL(url).openConnection();
            con.setConnectTimeout(timeout);
            con.setRequestMethod(method);

            if (userId != null && userToken != null) {
                String basicAuth = userId + ":" + userToken;
                basicAuth = "Basic " + Base64.encodeToString(basicAuth.getBytes(), Base64.NO_WRAP);
//                Log.v(TAG, "Auth: " + basicAuth);
                con.setRequestProperty("Authorization", basicAuth);
            }

            con.setDoInput(true);
            if (outputJson != null) {
                con.setDoOutput(true);
                con.setChunkedStreamingMode(0);
                IOUtil.writeStream(outputJson, con.getOutputStream());
            }

            con.connect();

            Log.i(TAG, "HttpStatus: " + con.getResponseCode());
            httpStatus = con.getResponseCode();
            if (httpStatus == 200) {
                result = IOUtil.readStream(con.getInputStream());
                Log.i(TAG, "Result: " + result);
            }
            disconnect(con);
            handler.handleResponse(httpStatus, result);
        } catch (MalformedURLException e) {
            e.printStackTrace();
            disconnect(con);
            handler.handleConnectionError();
        } catch (IOException e) {
            e.printStackTrace();
            disconnect(con);
            handler.handleConnectionError();
        }

        return null;
    }

    private void disconnect(final HttpsURLConnection con) {
        if (con != null) {
            con.disconnect();
        }
    }

    public NetworkBackgroundTask setTimeout(final int timeout) {
        this.timeout = timeout;
        return this;
    }

    public NetworkBackgroundTask setMethod(final String method) {
        this.method = method;
        return this;
    }

lg. Dagobert
 
kosmus hat dir die Frage beantwortet. Schaue dir sein Beispiel für DBRequestTask an, da steht alles was du brauchst. AsyncTask ist eine generische Klasse und bekommt 3 Objekt-Typen übergeben, die jeweils für ein Array von Objekten dieses Typs stehen. In Objekt 1 stehen die Übergabewerte (zum Beispiel Parameter, URLs, whatever) in Objekt 2 die Progress-Objekte, die den Progress verwalten (z.B. Integer/Float für Statusanzeigen) und in Objekt 3 die Rückgabewerte (z.B. der Response-Text eines Requests). Das Task wird, wie der Name schon sagt asynchron ausgeführt, d.h. parallel zum UIThread ausgeführt. Wenn du den Task direkt mit (ACHTUNG PSEUDOCODE):
Code:
AsyncTask myTask;
myTask.execute("my_funny_url").get();
ausführst, wird in der Programmausführung gewartet, bis die Response da ist. Sollte man aber nur machen, wenn man unbedingt muss.
 
Das mit dem get würde ich vermeiden, weil du dann wieder den main thread halten lässt (vermute ich, habe das noch nie gemacht). Ich vermute wenn du mein beispiel nicht verstehst, dann wirst du Dagoberts code auch nicht verstehen.

Ich kann später (vermutlich morgen) nochmal ein kommentiertes Beispiel posten.
 
Ich wäre dir sehr dankbar...

Oder könnte ich mit einem von euch kurz skypen, dass ich endlich weiterkomme? Bin gerade echt deprimiert :(
 
Also ich Poste jetzt einen Lösungsansatz der sich im Grunde nicht von meinem DB Beispiel unterscheidet, ich versuche aber zu erläutern, warum ich es so mache.

Die Lösung braucht ein Interface, ein ASynctask und eine Activity.

1. Das Interface
Code:
public interface StringResultHandler {
	public void handleStringResult(String result);
}
Wozu braucht man ein Interface?
Um das richtig verstehen muss man Vererbung und die Besonderheiten der Vererbung in Java verstehen. Eine Klasse hat die Attribute und Methoden seiner Elternklasse und auch die Attribute und Methoden von deren Elternklasse (seiner Großelternklasse). Methoden und Attribute können so über beliebig viele Generationen vererbt werden. Das Besondere bei Java ist jedoch, dass eine Klasse nur eine Elternklasse haben kann. Möchte man sicher stellen, dass mehrere Klassen, die keine gemeinsame Elternklasse haben, trotzdem eine bestimmte Methode anbieten, kann man dies mit einem Interface sicherstellen. Alle Klassen die ein Interface implementieren, müssen die im Interface genannten Methoden bereitstellen.
Bei Android haben wir das Problem das unsere UI Klassen in der Regel bereits von Activity erben, damit wir sicherstellen können, das unsere Activities auch eine Methode Verarbeitung der Antwort des Servers haben, brauchen wir daher dieses Interface.

2. Der ASyncTask

Code:
public class HttpPostTask extends AsyncTask<String, String, String> {
	private StringResultHandler handler;

	public HttpPostTask(StringResultHandler handler) {
		super();
		this.handler = handler;
	}

	@Override
	protected String doInBackground(String... params) {
		String ergebnis = "";
		try {
			if (params.length % 2 == 0)
				throw new Exception("Gerade Zahl an params ist nicht logisch");
			URL url = new URL(params[0]);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setReadTimeout(10000);
			conn.setConnectTimeout(15000);
			conn.setRequestMethod("POST");
			conn.setDoInput(true);
			conn.setDoOutput(true);
			if (params.length > 1) {
				StringBuilder postDaten = new StringBuilder();
				boolean erster = true;
				for (int i = 1; i < params.length; i = i + 2) {
					if (erster) {
						erster = false;
					} else {
						postDaten.append("&");
					}
					postDaten.append(URLEncoder.encode(params[i], "UTF-8"));
					postDaten.append("=");
					postDaten.append(URLEncoder.encode(params[i + 1], "UTF-8"));
				}
				OutputStream os = conn.getOutputStream();
				BufferedWriter writer = new BufferedWriter(
						new OutputStreamWriter(os, "UTF-8"));
				writer.write(postDaten.toString());
				writer.flush();
				writer.close();
				os.close();
			}
			conn.connect();
			StringBuilder antwort = new StringBuilder("");
			InputStream inputStream = conn.getInputStream();
			InputStreamReader reader = new InputStreamReader(inputStream,
					"UTF-8");
			char[] buffer = new char[128];
			while (reader.read(buffer) > 0) {
				antwort.append(buffer);
			}
			inputStream.close();
			ergebnis = antwort.toString();
		} catch (Exception e) {
			
			ergebnis = "Fehler: " + e.getClass().getName();
		}
		return ergebnis;
	}

	@Override
	protected void onPostExecute(String result) {
			handler.handleStringResult(result);

	}
}
AsyncTask ist eine abstrakte Klasse, die dafür gedacht ist, Sachen parallel zum main thread (sozusagen im Hintergrund) auszuführen. Ruft man auf einem Objekt der Klasse AsyncTask (bzw. einer Kindklasse von AsyncTask) die Methode execute(...) auf werden im Hintergrund verschiedene Methoden aufgerufen. In unserem Fall zunächst doInBackground und danach onPostExecute (es gibt noch weitere z. B. onProgressUpdate aber die sind für dieses Beispiel nicht nötig.
in der doInBackground wird die Verbindung zum Server hergestellt und die Antwort in einem String gespeichert. Die Parameter der Methode, sind die Parameter die man beim Aufruf von execute mitgibt. Als ersten Parameter erwartet die Methode einen die URL des Servers als String, und danach die Werte, die per POST an den Server geschickt werden sollen. Die Werte sind dabei immer als name-value Paar zu übergeben.

Unser HttpPostTask braucht zudem noch einen StringResultHandler, der das Ergebnis verarbeitet. Das Ergebnis wird in der onPostExecute an den Handler übergeben. In diesem Beispiel wird der Handler die LoginActivity sein, die das Interface StringResultHandler implementiert.

3. Die Activity:
Diese Klasse kann ich nur andeuten, da ich nicht weis, wie genau deine Verarbeitung aussehen wird. Aber vom Grundsatz:

Code:
public class MainActivity extends Activity implements StringResultHandler  {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//Was man so onCreate noch machen will
	}

	public void onClick(View view) {

		//Usereingaben aus den EditTexts auslesen oder ähnliches

		(new HttpPostTask(this)).execute("http://test/test.php", "user",
		"kosmus", "password", "vollGeheim");
	}
	public void handleStringResult(String result) {
		//ERGEBNISSTRING Verarbeiten
	}
}
Der AsyncTask wird in der onClick methode gestartet und ruft dann die handleStringResult Methode auf, wenn er fertig ist.
Das besondere an der Activity ist das "implements StringResultHandler". Dies sorgt dafür, das der AsyncTask sozusagen weiß, das die Activity die Methode handleStringReult hat.
 

Ähnliche Themen

S
Antworten
8
Aufrufe
511
swa00
swa00
F
Antworten
0
Aufrufe
836
FlorianAlfredo
F
Zurück
Oben Unten