http://www.androiddeft.com/2018/01/21/login-registration-android-php-mysql/
Most of the Android applications need to manage users. The first thing most of the apps ask during the launch is to either login or register as a member. App developers make use of back-end servers and remote databases to store user data so that all user details can be accessed from one place and data can be synced with the application, whenever the user logs in.
In this tutorial, I will discuss on how we can implement simple login and registration in Android. I will make use of PHP for backend scripting and MySQL database for storing the data.
In my previous article on Connecting Android App to Remote Database, I have explained in detail about connecting to a remote database using PHP and MySQL. You can go through the article to have a basic understanding of how to set up WAMP server and creating the database.
Creating MySQL Database and Table
1. Login to phpMyAdmin (http://localhost/phpmyadmin/) in your browser and create a new database named androiddeft (if not present already).
2. Run the below SQL to create the table named member:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | -- -- Database: `androiddeft` -- -- -------------------------------------------------------- -- -- Table structure for table `member` -- CREATE TABLE `member` ( `user_id` int(11) NOT NULL, `username` varchar(50) NOT NULL, `full_name` varchar(50) NOT NULL, `password_hash` varchar(256) NOT NULL, `salt` varchar(256) NOT NULL, `created_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- -- Indexes for dumped tables -- -- -- Indexes for table `member` -- ALTER TABLE `member` ADD PRIMARY KEY (`user_id`), ADD UNIQUE KEY `username` (`username`); -- -- AUTO_INCREMENT for dumped tables -- -- -- AUTO_INCREMENT for table `member` -- ALTER TABLE `member` MODIFY `user_id` int(11) NOT NULL AUTO_INCREMENT; COMMIT; -- -------------------------------------------------------- |
Scripting Backend PHP
1. Open www folder of WAMP server and create a new folder named member. This folder will house all the PHP files used in this tutorial.
2. Now create a sub-folder named db inside member folder.
3. Create a PHP file called db_connect.php and add the following code. This will help in establishing the connection to the MySQL database. You may update database details accordingly.
| <?php define('DB_USER', "root"); // db user define('DB_PASSWORD', ""); // db password (mention your db password here) define('DB_DATABASE', "androiddeft"); // database name define('DB_SERVER', "localhost"); // db server $con = mysqli_connect(DB_SERVER,DB_USER,DB_PASSWORD,DB_DATABASE); // Check connection if(mysqli_connect_errno()) { echo "Failed to connect to MySQL: " . mysqli_connect_error(); } ?> |
4. Create a file named functions.php inside the member folder. This contains functions for checking whether the user exists not, functions for generating password salt and hashes. Here I have made use of random salt generator so that a unique salt and hash is generated for each user even if 2 people share the same password. This adds an additional level of security and prevents hackers from making use of reverse lookup tables.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | <?php $random_salt_length = 32; /** * Queries the database and checks whether the user already exists * * @param $username * * @return */ function userExists($username){ $query = "SELECT username FROM member WHERE username = ?"; global $con; if($stmt = $con->prepare($query)){ $stmt->bind_param("s",$username); $stmt->execute(); $stmt->store_result(); $stmt->fetch(); if($stmt->num_rows == 1){ $stmt->close(); return true; } $stmt->close(); } return false; } /** * Creates a unique Salt for hashing the password * * @return */ function getSalt(){ global $random_salt_length; return bin2hex(openssl_random_pseudo_bytes($random_salt_length)); } /** * Creates password hash using the Salt and the password * * @param $password * @param $salt * * @return */ function concatPasswordWithSalt($password,$salt){ global $random_salt_length; if($random_salt_length % 2 == 0){ $mid = $random_salt_length / 2; } else{ $mid = ($random_salt_length - 1) / 2; } return substr($salt,0,$mid - 1).$password.substr($salt,$mid,$random_salt_length - 1); } ?> |
5. Create another PHP file called register.php. This takes username, password and full name as parameters checks whether the username is already taken. If not, it registers the user and prints a success response. If any errors occur, then it sets it in the response.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | <?php $response = array(); include 'db/db_connect.php'; include 'functions.php'; //Get the input request parameters $inputJSON = file_get_contents('php://input'); $input = json_decode($inputJSON, TRUE); //convert JSON into array //Check for Mandatory parameters if(isset($input['username']) && isset($input['password']) && isset($input['full_name'])){ $username = $input['username']; $password = $input['password']; $fullName = $input['full_name']; //Check if user already exist if(!userExists($username)){ //Get a unique Salt $salt = getSalt(); //Generate a unique password Hash $passwordHash = password_hash(concatPasswordWithSalt($password,$salt),PASSWORD_DEFAULT); //Query to register new user $insertQuery = "INSERT INTO member(username, full_name, password_hash, salt) VALUES (?,?,?,?)"; if($stmt = $con->prepare($insertQuery)){ $stmt->bind_param("ssss",$username,$fullName,$passwordHash,$salt); $stmt->execute(); $response["status"] = 0; $response["message"] = "User created"; $stmt->close(); } } else{ $response["status"] = 1; $response["message"] = "User exists"; } } else{ $response["status"] = 2; $response["message"] = "Missing mandatory parameters"; } echo json_encode($response); ?> |
6. Finally create a file for login, namely login.php. Here we compare the stored hash with the hash generated from the password entered by the user using the stored salt. If password matches then we set a success response and a failure response otherwise.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <?php $response = array(); include 'db/db_connect.php'; include 'functions.php'; //Get the input request parameters $inputJSON = file_get_contents('php://input'); $input = json_decode($inputJSON, TRUE); //convert JSON into array //Check for Mandatory parameters if(isset($input['username']) && isset($input['password'])){ $username = $input['username']; $password = $input['password']; $query = "SELECT full_name,password_hash, salt FROM member WHERE username = ?"; if($stmt = $con->prepare($query)){ $stmt->bind_param("s",$username); $stmt->execute(); $stmt->bind_result($fullName,$passwordHashDB,$salt); if($stmt->fetch()){ //Validate the password if(password_verify(concatPasswordWithSalt($password,$salt),$passwordHashDB)){ $response["status"] = 0; $response["message"] = "Login successful"; $response["full_name"] = $fullName; } else{ $response["status"] = 1; $response["message"] = "Invalid username and password combination"; } } else{ $response["status"] = 1; $response["message"] = "Invalid username and password combination"; } $stmt->close(); } } else{ $response["status"] = 2; $response["message"] = "Missing mandatory parameters"; } //Display the JSON response echo json_encode($response); ?> |
Creating Android Project
Now we will see how can we make use of the PHP login and register API’s we have created. We will be making use of Volley library for performing network calls.
1. Create a project in Android Studio with the name Login and Registration. Name the activity as LoginActivity.
2. Add Volley dependency to build.gradle file of app module:
| dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.0.2' implementation 'com.android.volley:volley:1.0.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' } |
3. Open AndroidManifest.xml and Internet permission:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.androiddeft.loginandregistration"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".LoginActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:theme="@style/Theme.AppCompat.Light.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".RegisterActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:theme="@style/Theme.AppCompat.Light.NoActionBar" /> <activity android:name=".DashboardActivity" /> </application> </manifest> |
4. Create a POJO class named User to hold the user details:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package com.androiddeft.loginandregistration; import java.util.Date; /** * Created by Abhi on 20 Jan 2018 020. */ public class User { String username; String fullName; Date sessionExpiryDate; public void setUsername(String username) { this.username = username; } public void setFullName(String fullName) { this.fullName = fullName; } public void setSessionExpiryDate(Date sessionExpiryDate) { this.sessionExpiryDate = sessionExpiryDate; } public String getUsername() { return username; } public String getFullName() { return fullName; } public Date getSessionExpiryDate() { return sessionExpiryDate; } } |
5. Update the string resources to be used in this project in string.xml:
| <resources> <string name="app_name">LoginAndRegistration</string> <string name="username">Username</string> <string name="password">Password</string> <string name="sign_in">Sign In</string> <string name="register">Register</string> <string name="full_name">Full Name</string> <string name="confirm_password">Confirm Password</string> <string name="already_having_account">Already Having an Account?</string> <string name="log_in">Log In</string> <string name="not_having_account">Not Having an Account?</string> <string name="sign_up">Sign Up</string> <string name="logout">Logout</string> </resources> |
6. Create a class named SessionHandler and update it with the following code. This helps in handling user sessions by performing actions like logging in the user, fetching user details, logging out user etc. Here I have made use of shared preferences to maintain the user session. Also here we have a method to check if the user is already logged in so that he can be redirected to dashboard screen.
I have set a session expiry time of 7 days so that user gets logged out after 7 days of login. You can update this value based on your need.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | package com.androiddeft.loginandregistration; import android.content.Context; import android.content.SharedPreferences; import java.util.Date; /** * Created by Abhi on 20 Jan 2018 020. */ public class SessionHandler { private static final String PREF_NAME = "UserSession"; private static final String KEY_USERNAME = "username"; private static final String KEY_EXPIRES = "expires"; private static final String KEY_FULL_NAME = "full_name"; private static final String KEY_EMPTY = ""; private Context mContext; private SharedPreferences.Editor mEditor; private SharedPreferences mPreferences; public SessionHandler(Context mContext) { this.mContext = mContext; mPreferences = mContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); this.mEditor = mPreferences.edit(); } /** * Logs in the user by saving user details and setting session * * @param username * @param fullName */ public void loginUser(String username, String fullName) { mEditor.putString(KEY_USERNAME, username); mEditor.putString(KEY_FULL_NAME, fullName); Date date = new Date(); //Set user session for next 7 days long millis = date.getTime() + (7 * 24 * 60 * 60 * 1000); mEditor.putLong(KEY_EXPIRES, millis); mEditor.commit(); } /** * Checks whether user is logged in * * @return */ public boolean isLoggedIn() { Date currentDate = new Date(); long millis = mPreferences.getLong(KEY_EXPIRES, 0); /* If shared preferences does not have a value then user is not logged in */ if (millis == 0) { return false; } Date expiryDate = new Date(millis); /* Check if session is expired by comparing current date and Session expiry date */ return currentDate.before(expiryDate); } /** * Fetches and returns user details * * @return user details */ public User getUserDetails() { //Check if user is logged in first if (!isLoggedIn()) { return null; } User user = new User(); user.setUsername(mPreferences.getString(KEY_USERNAME, KEY_EMPTY)); user.setFullName(mPreferences.getString(KEY_FULL_NAME, KEY_EMPTY)); user.setSessionExpiryDate(new Date(mPreferences.getLong(KEY_EXPIRES, 0))); return user; } /** * Logs out user by clearing the session */ public void logoutUser(){ mEditor.clear(); mEditor.commit(); } } |
7. Create a new XML file inside the drawable folder with the name ic_lock_outline_white.xml and update it with the following SVG code. This is a vector graphics icon used to display lock symbol in the login screen
| <vector android:height="96dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="96dp" xmlns:android="http://schemas.android.com/apk/res/android"> <group> <clip-path android:pathData="M0,0h24v24H0V0z M 0,0"/> <path android:fillColor="#CCC" android:pathData="M12 17c1.1 0 2,-.9 2,-2s-.9,-2,-2,-2,-2 .9,-2 2 .9 2 2 2zm6,-9h-1V6c0,-2.76,-2.24,-5,-5,-5S7 3.24 7 6v2H6c-1.1 0,-2 .9,-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2,-.9 2,-2V10c0,-1.1,-.9,-2,-2,-2zM8.9 6c0,-1.71 1.39,-3.1 3.1,-3.1s3.1 1.39 3.1 3.1v2H8.9V6zM18 20H6V10h12v10z"/> </group> </vector> |
8. Update the activity_login.xml (this is the activity_main.xml file generated while creating the project) file with the below code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#304769" android:gravity="center_vertical" android:orientation="vertical" android:padding="50dp" tools:context="com.androiddeft.loginandregistration.LoginActivity"> <ImageView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="20dp" android:src="@drawable/ic_lock_outline_white" /> <EditText android:id="@+id/etLoginUsername" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:background="@android:color/white" android:hint="@string/username" android:inputType="textPersonName" android:padding="10dp" /> <EditText android:id="@+id/etLoginPassword" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:background="@android:color/white" android:hint="@string/password" android:inputType="textPassword" android:padding="10dp" /> <Button android:id="@+id/btnLogin" android |