Tutorial: build an android application with secure user authentication

Android authentication with retrofit on json data

I have Json data on firebase like this;

[{"Password":123,"UserName":"User1"},{"Password":34,"UserName":"User2"}]

I want to implement authentication depend on this data but problem is that I can only post on Json data for example;

user will enter new data UserName = User3 and Password = 44; my Json data will be

[{"Password":123,"UserName":"User1"},{"Password":34,"UserName":"User2"},{"Password":44,"UserName":"User3"}]

This is not my purpose i want to check the data and return “valid or not” in my originally data.

Problem is that; i want to check in data availability when i send username and password.If exist return response is “success” if not response is “error”.

My User Class is that;

public class User {

@SerializedName("UserName")
public String UserName;

@SerializedName("Password")
public String Password;

public User(String UserName, String Password) {
    this.UserName = UserName;
    this.Password = Password;
 }
}

Logon Result class is that;

public class LogonResult {
@SerializedName("HasErrors")
public boolean hasError;

@SerializedName("Message")
public String message;

@SerializedName("LoggedUser")
public User loggedUser;
}

Retrofit Service like that;

public interface CensService {

@POST("/User.json")
Call<LogonResult> login(@Body User userInfo);
}

And here is the main method;

  final String user = usernameInput.getText().toString().trim();
  final String pass = passwordInput.getText().toString().trim();
  User usr = new User(user,pass);
  final CensClient client = new CensClient();
  final CensService apiService = client.getClient(LoginActivity.this).create(CensService.class);
        Call<LogonResult> userCall = apiService.login(usr);
        Log.w("request",userCall.request().toString());

        userCall.enqueue(new Callback<LogonResult>() {

            @Override
            public void onResponse(Call<LogonResult> call, Response<LogonResult> response) {
                if(response.isSuccessful()) {
                    //(response.body().toString());
                }else{
                    //(response.message());
                }
            }

            @Override
            public void onFailure(Call<LogonResult> call, Throwable t) {
               //
            }
        });

Android login with json

I’m try to do the login on my company website using their API.
Basically when I run the Login I received response null, I don’t know if is the server that doesn’t receive my request or something else, because I tested my json object and is fine, I tested the POST request with username and password like parameters to my server and it works properly, so I don’t know why my app doesn’t comunicate with the server, it still come up Internal Server Error.

Here my code:

public void login(View button) {
    EditText userEmailField = (EditText) findViewById(R.id.userEmail);
    mUserEmail = userEmailField.getText().toString();
    EditText userPasswordField = (EditText) findViewById(R.id.userPassword);
    mUserPassword = userPasswordField.getText().toString();

    if (mUserEmail.length() == 0 || mUserPassword.length() == 0) {
        // input fields are empty
        Toast.makeText(this, "Please complete all the fields",
            Toast.LENGTH_LONG).show();
        return;
    } else {
        LoginTask loginTask = new LoginTask(LoginActivity.this);
        loginTask.setMessageLoading("Logging in...");
        loginTask.execute(LOGIN_API_ENDPOINT_URL);
    }
}
private class LoginTask extends UrlJsonAsyncTask {
    public LoginTask(Context context) {
        super(context);
    }

    @Override
    protected JSONObject doInBackground(String... urls) {
        @SuppressWarnings("deprecation")
        DefaultHttpClient client = new DefaultHttpClient();
        @SuppressWarnings("deprecation")
        HttpPost post = new HttpPost(urls[0]);
        JSONObject holder = new JSONObject();
        JSONObject userObj = new JSONObject();
        String response = "";
        JSONObject json = new JSONObject();

        try {
            try {
                // setup the returned values in case
                // something goes wrong
                json.put("success", false);
                json.put("info", "Something went wrong. Retry!");
                // add the user email and password to
                // the params
                userObj.put("username", mUserEmail);
                userObj.put("password", mUserPassword);
                holder.put("user", userObj);
                @SuppressWarnings("deprecation")
                StringEntity se = new StringEntity(holder.toString());
                post.setEntity(se);

                // setup the request headers
                post.setHeader("Accept", "*/*");
                post.setHeader("Content-Type", "application/json");

                @SuppressWarnings("deprecation")
                ResponseHandler<String> responseHandler = new BasicResponseHandler();
                response = client.execute(post, responseHandler);
                String code_response=response.getBytes().toString();
                json = new JSONObject(response);
                json.put(code_response, "");

            } catch (HttpResponseException e) {
                e.printStackTrace();
                Log.e("ClientProtocol", ""   e);
                Log.e("Localize", e.getLocalizedMessage());
                Log.e("MESSAGE", e.getMessage());
                e.printStackTrace();
                json.put("info", "Email and/or password are invalid. Retry!");
            } catch (IOException e) {
                e.printStackTrace();
                Log.e("IO", ""   e);
            }
        } catch (JSONException e) {
            e.printStackTrace();
            Log.e("JSON", ""   e);
        }

        return json;
    }

    @Override
    protected void onPostExecute(JSONObject json) {
        try {
            if (json.getBoolean("success")) {
                // everything is ok
                SharedPreferences.Editor editor = mPreferences.edit();
                // save the returned auth_token into
                // the SharedPreferences
                editor.putString("AuthToken", json.getJSONObject("data").getString("auth_token"));
                editor.commit();

                // launch the HomeActivity and close this one
                Intent intent = new Intent(getApplicationContext(), Login_Page.class);
                startActivity(intent);
                finish();
            }
            Toast.makeText(context, json.getString("info"), Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            // something went wrong: show a Toast
            // with the exception message
            Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
        } finally {
            super.onPostExecute(json);
        }
    }
}

This is the logCat:

09-14 13:05:47.385  13798-13798/com.example E/SpannableStringBuilder﹕ SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
09-14 13:05:47.385  13798-13798/com.example E/SpannableStringBuilder﹕ SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
09-14 13:06:05.044  13798-14159/com.example E/ClientProtocol﹕ org.apache.http.client.HttpResponseException: Internal Server Error
09-14 13:06:05.044  13798-14159/com.example E/Localize﹕ Internal Server Error
09-14 13:06:05.044  13798-14159/com.example E/MESSAGE﹕ Internal Server Error

Thanks guys

Android web server authentication with jsonrpc

If you want to do a simple API key then in every JSON call, the client would pass the auth key and in each method on the php side would get the key and authenticate it.

If you wanted to do session type authentication, you would have to first call an authenticate method that would return the session_id to the client. The client would then send the session key in every subsequent method. Within the methods, the server could then check the session key.

I have looked for a way to cleanly abstract authentication out of the actual json RPC methods, but if you use Zend_Json_Server directly, there is no way to do it. You may be able to extend it, but to me, it wasn’t worth the hassle.

Create the “notes” android application

Open the project using Android Studio and it should be configured to compile.

The Gradle dependency for including the Stormpath SDK is:

In the Application class (NotesApp.java), add the following in the onCreate method:

Optionally, for debug information, add this method before StormpathConfiguration‘s method calls.

Installing stormpath

If you’d like to see the finished version of the project, check out the finished branch.

Restful api authentication for an android app

Based off your comments, the instructions tells you to use Resource Owner Password Credentials Grant. You can see an example request in the spec.

 POST /token HTTP/1.1
 Host: server.example.com
 Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
 Content-Type: application/x-www-form-urlencoded

 grant_type=password&username=johndoe&password=A3ddj3w

The only thing that may seem odd (if you’ve never encountered it), is the Authorization header value. Read up on Basic Authentication. Basically the czZCaGRSa3F0MzpnWDFmQmF0M2JW is a base64 encoding of username:password (actually <client_id>:<client_secret>).

Without using any outside libraries (just standard Java libs) to make the request, you might have something like

String formData = "username=<uname>&password=<pass>&grant_type=password";
String header = "Basic "   Base64.encodeAsString("<client_id>:<client_secret>");

HttpURLConnection connection
                = (HttpURLConnection) new URL(tokenUrl).openConnection();
connection.setDoOutput(true);
connection.addRequestProperty("Authorization", header);
connection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestMethod("POST");
connection.setRequestProperty("charset", "utf-8");
connection.setRequestProperty("Content-Length", Integer.toString(formData.length()));

OutputStream out = connection.getOutputStream();
out.write(formData.getBytes(StandardCharsets.UTF_8));

InputStream in = connection.getInputStream();
AccessToken token = new ObjectMapper().readValue(in, AccessToken.class);
System.out.println(token);

out.close();
in.close();

The Base64 I used is not a standard library class. Also the ObjectMapper is not a standard library class. I just used it to parse the token response to the AccessToken class. You can use any parser you like. The AccessToken class just has all the possible token values

public class AccessToken {
    public String access_token;
    public String refresh_token;
    public long expires_in;
    public String token_type;
    public String scope;
}

From there, once you have the token, any resource requests you want to make, you just need to add an Authorization header with Bearer <access_token>.

What’s next?

Try the iOS SDK – If you (or a friend) is into iOS development, try following through the iOS tutorial for Stormpath Notes. Since the app will make requests against the same API, you’ll notice that you can save your notes on one device, and open them up on the other!

Build a Backend with Stormpath – Try building this API from scratch! Stormpath Notes’ example backend is just 45 lines of code! See code on GitHub. Alternatively, try getting started with express-stormpath or stormpath-laravel (more integrations coming soon!)

Объясните принципы разработки авторизации в android

буду делать на retrofit так как по нем больше информации и видосов

Не лучший ход мыслей.

во всех случаях каждый из разработчиков создает кучу классов для обычного логина

Да, это действительно так. С тем же Volley или голым OkHttp гораздо проще сделать обычный запрос, но не проще с этим работать.

Но чаще пользуются Retrofit. Почему? Потому что он очень удобен.

Чуть позже, когда помимо вопросов как написать тоже самое, но на десять строчек кода короче, и вообще когда помимо количества строчек кода или внешней простоты реализации для вас появятся ещё какие-нибудь пункты, соответствие которым будет для вас весомым, вы поймёте, почему по Retrofit «куча видосов» — отчасти, из-за некоторой общей популярности, потому что работа с сетью у всех и ассоциируется с Retrofit, — но и популярность пришла не зря.

Да и насчёт простоты кода тоже можно поспорить — вам всего пару шагов проделать надо, и в случае сильного увеличения количества запросов, в случае усложнения этих самых запросов, количество кода будет сильно уменьшаться, и с Volley-ами и OkHttp вы устанете переписывать одно и то же везде, или костылить свои врапперы.

Пример: инициализируем

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com")
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build();

Затем создаём интерфейс для работы с API:

interface MyExampleApi {

    @GET("/get-something")
    Call<MyExampleResponse> getSomething(@Query String someParameter);
}

Далее вам нужно десериализованный POJO для ответа от сервера написать, и всё:

class MyExampleResponse {
    private String topSecretInfo;

    // constructors, getters, setters
}

На этом «куча классов» закончилась. Удобно, разве нет? Теперь вызываем:

MyExampleApi api = retrofit.create(MyExampleApi.class)
api.getSomething("wow! such retrofit").enqueue(new Callback<MyExampleResponse> { /*  обработка */ });

Итого — у вас в коде пара строчек потрачено на логику по работе с сетью, и помимо этого ещё пара коротеньких файлов.
Это я ещё не говорил про тестирование, DI и так далее.

Какая мораль? Если вы слабо знакомы с HTTP-протоколом, да и вообще HTTP, POST и GET для вас ругательства капсом — тогда не парьтесь пока что 🙂
В таком случае лучше изучите сначала теорию по этим вопросам. Не нужно просто брать и копировать из тырнетов огромные простыни, подменяя лишь маленький кусочек кода в серединке, который и является вашей бизнес-логикой. Вы должны сначала понимать каждую буковку, которую пишете — потом понимать цель, с которой пишется эта строчка кода и что она в итоге делает — а уже потом среди аналогов выбирать, какой же из них делает это удобнее всего.

Самое главное — не делайте ничего бездумно, не копируйте первый попавшийся код потому что он «короче», «выглядит проще» или потому что «по нему видосиков куча».


Если к делу — о какой конкретно авторизации идёт речь? У вас свой API, или же вы хотите встроить авторизацию через популярные сервисы вроде VK, Google, Facebook и всё такое прочее?

В первом случае всё зависит от протоколов и способов аутентификации. Что вы используете: OAuth, накостыленная передача логинов и паролей прямо в теле запроса, Bearer-токены?

Во втором случае обычно используются SDK этих сервисов и вручную ничего не пишут. Но иногда (как например в случае с VK), готовые SDK крайне убогие или не обновлялись много лет и приходится писать вручную — тогда чаще всего одними запросами вы не отделаетесь, вам придется юзера самого заставить тыкать на кнопочки, предоставленные сервисом, и скорее всего через WebView.

Если же всё-таки вы всё делаете вручную, и бэкенд тоже ваш, тогда логика тоже довольно проста — в случае с Retrofit — создаёте интерфейс (вроде написанного выше MyExampleApi), помещаете туда методы с нужными сигнатурами, и выстраиваете свою логику. Юзер вводит пароль, или вы как-то сами его получаете, — вместе с логином шифруете его как-нибудь, или просто так отправляете в теле запроса — делаете обычный запрос с нужными заголовками, телом или query, и на основе ответа от сервера решаете, залогинился юзер или нет.


В общих словах всё выглядит просто. Ваш вопрос довольно развёрнутый, но не содержит конкретики — мой ответ тоже развёрнут и содержит всю необходимую информацию и даже сравнение. Больше конкретики по самой авторизации я не могу на данный момент написать, потому что конкретика вопроса не позволяет.


UPD:

Передача данных на сервер пока идет в формате json но потом вроде как планируется и передавать методом post.

А как вы планируете json передавать не через POST, в query как параметр при GET-запросе? Есть некоторые случаи такой логики, например при подписи запросов, но я сомневаюсь, что у вас это используется, а значит вам нужен именно POST-запрос.

Делается всё очень просто. Допустим, вам нужно отправить только логин и пароль на сервер, а в ответ придёт статус — залогинен юзер или нет. Тогда пример выше я преобразую:

Инициализация выглядит также. Интерфейс будет примерно такой:

interface AuthApi {

    @POST("/auth")
    Call<AuthResponse> authUser(@Body AuthRequestBody body);
}

Вот ваше тело запроса:

class AuthRequestBody {
    private String login, password;

    // constructor, getters, setters
}

Оно будет автоматически сериализовано в JSON с помощью GSON:

{ "login": "some string", "password": "some string" }

Допустим, вот ваш JSON-ответ от сервера:

{ "success": true }

Тогда ваш десериализованный класс может выглядеть так:

class AuthResponse {
    private boolean success;

    public boolean isUserLoggedIn() {
        return success;
    }
}

Это будет сделано автоматически GSON-ом, т.к. вы добавили GsonConverterFactory в билдере при инициализации ретрофита.

Допустим, из ваших двух EditText по нажатию на кнопку вы получаете логин и пароль юзера:

String login = loginEditText.getText();
String password = passwordEditText.getText();
AuthRequestBody body = new AuthRequestBody(login, password);

Тело запроса сформировано. Теперь проинициализируем интерфейс для работы с API:

AuthApi api = retrofit.create(AuthApi.class)
Call<AuthResponse> call = api.authUser(body);

И теперь обрабатываем ответ:

call.enqueue(new Callback<AuthResponse> {

    @Override
    public void onResponse(Call<AuthResponse> call, Response<AuthResponse> response) {
        if (response.isSuccessful()) {
            AuthResponse serverAnswer = response.body();

            if (serverAnswer.isUserLoggedIn()) {
                // Авторизация прошла успешно
                // можно, например, перейти на другую активити
            } else {
                // Авторизация не прошла
            }
        } else {
            // Ошибка при запросе
        }
    }

    @Override
    public void onFailure(Call<AuthResponse> call, Throwable t) {
        // Ошибка при запросе
    }
});

Вот, собственно, и вся логика.

Ваше тело запроса AuthRequestBody будет сериализовано в JSON, почитайте про то, как это работает, в частности про библиотеку GSON.

Похожее:  КАК ВОЙТИ В ЛИЧНЫЙ КАБИНЕТ БИЛАЙН ПО НОМЕРУ МОБИЛЬНОГО ТЕЛЕФОНА В СТАВРОПОЛЬСКОМ КРАЕ И КАКОВА ЕГО ЦЕЛЬ

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *