当我不使用 Firebase 身份验证时,如何使用 Cloud Firestore 中的 SharedPreferences 保存用户会话?
How do I save user session using SharedPreferences in Cloud Firestore when I'm not using Firebase authentication?
当用户在我的 Android 应用程序中注册时,他们的数据存储在一个集合中,其中文档 ID 是他们的电子邮件地址,这有助于找到用户。密码也存储在文档中。
因此,当用户登录时,他们输入的电子邮件将根据与该电子邮件匹配的文档 ID 进行检查,如果存在,它将登录并显示用户控制面板。
现在登录后,我需要以某种方式创建用户会话,以便用户在注销之前永远无法返回登录屏幕。万一应用程序崩溃或丢失网络连接,保存的用户会话将非常有用。
这是我的登录代码 activity:
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Patterns;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.basgeekball.awesomevalidation.AwesomeValidation;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.firestore.CollectionReference;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FirebaseFirestore;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import static com.basgeekball.awesomevalidation.ValidationStyle.BASIC;
public class SLogin extends AppCompatActivity
{
public static final String STUDENT_EMAIL = "student_email";
private EditText tEmail;
private EditText tPassword;
private String email = "";
private FirebaseFirestore db = FirebaseFirestore.getInstance();
private CollectionReference dbUsers;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_slogin);
tEmail= findViewById(R.id.Email);
tEmail.addTextChangedListener(new TextWatcher()
{
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
email = s.toString();
}
@Override
public void afterTextChanged(Editable s)
{
}
});
tPassword=findViewById(R.id.Password);
// Here you simply set saveEmail to empty string because at this moment when activity is
// created (onCreate) tEmail field is empty. To update this field dynamically you need set
// addTextChangedListener on tEmail.
// saveEmail=tEmail.getText().toString();
dbUsers = db.collection("Students");
}
//Validation Method
private boolean validate()
{
AwesomeValidation mAwesomeValidation = new AwesomeValidation(BASIC);
mAwesomeValidation.addValidation(tEmail, Patterns.EMAIL_ADDRESS, "Invalid Email Address");
String regexPassword = "(?=.*[a-z])(?=.*[A-Z])(?=.*[\d])(?=.*[~`!@#\$%\^&\*\(\)\-_\+=\{\}\[\]\|\;:\"<>,./\?]).{6,}";
mAwesomeValidation.addValidation(tPassword, regexPassword, "Use 6 or more characters with a mix of upper & lower letters, numbers & symbols");
return mAwesomeValidation.validate();
}
//Method to check password matching or not
private void passCheck(@NonNull DocumentSnapshot snapshot)
{
final String uPass = tPassword.getText().toString();
final String storedPass = snapshot.getString("password");
if (storedPass != null && storedPass.equals(uPass))
{
Intent intent = new Intent(SLogin.this, StudentCP.class);
intent.putExtra(STUDENT_EMAIL, email);
startActivity(intent);
}
else
{
Toast.makeText(SLogin.this, "Invalid Password!", Toast.LENGTH_LONG).show();
}
}
public void sLogin(View v)
{
if (validate())
{
DocumentReference dbDocs = dbUsers.document(email);
dbDocs.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>()
{
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task)
{
if (task.isSuccessful())
{
DocumentSnapshot document = task.getResult();
if (document != null && document.exists())
{
//Toast.makeText(SLogin.this, "You are registered", Toast.LENGTH_LONG).show();
// Improved password checking because at first glance I
// don't see why you call db fetch again to get document
// because if we are here that means we got matching data
// and now we only need to check if password match. No need
// to call get on db again.
//
// It's possible to even more optimize calls to DB in case
// of wrongly typed password. We can cache input email and
// returned password for that email so in case if user
// doesn't change email, but types only password again we
// can compare newly typed password with cached password
// from previous request so we don't make again new DB
// request to simply get again same saved password.
//
// Currently I haven't implemented caching. It's only idea
// to think about in future.
passCheck(document);
}
else
{
Toast.makeText(SLogin.this, "You are not registered", Toast.LENGTH_LONG).show();
}
}
else
{
Toast.makeText(SLogin.this, "Unable to connect to database", Toast.LENGTH_LONG).show();
}
}
});
}
}
public void nUser(View v)
{
Intent intent = new Intent(SLogin.this, RegisterActivity.class);
startActivity(intent);
}
}
为获得最佳实践,请添加以下内容 class
public class PrefUtilities {
private SharedPreferences preferences;
Context context;
private PrefUtilities(Context context) {
preferences = PreferenceManager.getDefaultSharedPreferences(context);
this.context = context;
}
public static PrefUtilities with(Context context){
return new PrefUtilities(context);
}
public void setUserLogin(boolean isUserLogedin){
preferences.edit().putBoolean(context.getString(R.string.pref_key_user_status),isUserLogedin).apply();
}
public boolean isUserLogedin(){
return preferences.getBoolean(context.getString(R.string.pref_key_user_status),false);
}
}
在 onCreate
方法中检查登录状态
if(PrefUtilities.with(this).isUserLogedin()){
Intent intent = new Intent(SLogin.this, StudentCP.class);
intent.putExtra(STUDENT_EMAIL, email);
startActivity(intent);
}
在 passCheck
方法中保存登录状态
private void passCheck(@NonNull DocumentSnapshot snapshot)
{
final String uPass = tPassword.getText().toString();
final String storedPass = snapshot.getString("password");
if (storedPass != null && storedPass.equals(uPass))
{
PrefUtilities.with(this).setUserLogin(true);
Intent intent = new Intent(SLogin.this, StudentCP.class);
intent.putExtra(STUDENT_EMAIL, email);
startActivity(intent);
}
else
{
Toast.makeText(SLogin.this, "Invalid Password!", Toast.LENGTH_LONG).show();
}
}
当用户注销时使用下面的方法更改 SharedPreferences
PrefUtilities.with(this).setUserLogin(false);
您还可以在 PrefUtilities
class 保存用户邮箱中添加其他方法
当用户在我的 Android 应用程序中注册时,他们的数据存储在一个集合中,其中文档 ID 是他们的电子邮件地址,这有助于找到用户。密码也存储在文档中。
因此,当用户登录时,他们输入的电子邮件将根据与该电子邮件匹配的文档 ID 进行检查,如果存在,它将登录并显示用户控制面板。
现在登录后,我需要以某种方式创建用户会话,以便用户在注销之前永远无法返回登录屏幕。万一应用程序崩溃或丢失网络连接,保存的用户会话将非常有用。
这是我的登录代码 activity:
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Patterns;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.basgeekball.awesomevalidation.AwesomeValidation;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.firestore.CollectionReference;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FirebaseFirestore;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import static com.basgeekball.awesomevalidation.ValidationStyle.BASIC;
public class SLogin extends AppCompatActivity
{
public static final String STUDENT_EMAIL = "student_email";
private EditText tEmail;
private EditText tPassword;
private String email = "";
private FirebaseFirestore db = FirebaseFirestore.getInstance();
private CollectionReference dbUsers;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_slogin);
tEmail= findViewById(R.id.Email);
tEmail.addTextChangedListener(new TextWatcher()
{
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
email = s.toString();
}
@Override
public void afterTextChanged(Editable s)
{
}
});
tPassword=findViewById(R.id.Password);
// Here you simply set saveEmail to empty string because at this moment when activity is
// created (onCreate) tEmail field is empty. To update this field dynamically you need set
// addTextChangedListener on tEmail.
// saveEmail=tEmail.getText().toString();
dbUsers = db.collection("Students");
}
//Validation Method
private boolean validate()
{
AwesomeValidation mAwesomeValidation = new AwesomeValidation(BASIC);
mAwesomeValidation.addValidation(tEmail, Patterns.EMAIL_ADDRESS, "Invalid Email Address");
String regexPassword = "(?=.*[a-z])(?=.*[A-Z])(?=.*[\d])(?=.*[~`!@#\$%\^&\*\(\)\-_\+=\{\}\[\]\|\;:\"<>,./\?]).{6,}";
mAwesomeValidation.addValidation(tPassword, regexPassword, "Use 6 or more characters with a mix of upper & lower letters, numbers & symbols");
return mAwesomeValidation.validate();
}
//Method to check password matching or not
private void passCheck(@NonNull DocumentSnapshot snapshot)
{
final String uPass = tPassword.getText().toString();
final String storedPass = snapshot.getString("password");
if (storedPass != null && storedPass.equals(uPass))
{
Intent intent = new Intent(SLogin.this, StudentCP.class);
intent.putExtra(STUDENT_EMAIL, email);
startActivity(intent);
}
else
{
Toast.makeText(SLogin.this, "Invalid Password!", Toast.LENGTH_LONG).show();
}
}
public void sLogin(View v)
{
if (validate())
{
DocumentReference dbDocs = dbUsers.document(email);
dbDocs.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>()
{
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task)
{
if (task.isSuccessful())
{
DocumentSnapshot document = task.getResult();
if (document != null && document.exists())
{
//Toast.makeText(SLogin.this, "You are registered", Toast.LENGTH_LONG).show();
// Improved password checking because at first glance I
// don't see why you call db fetch again to get document
// because if we are here that means we got matching data
// and now we only need to check if password match. No need
// to call get on db again.
//
// It's possible to even more optimize calls to DB in case
// of wrongly typed password. We can cache input email and
// returned password for that email so in case if user
// doesn't change email, but types only password again we
// can compare newly typed password with cached password
// from previous request so we don't make again new DB
// request to simply get again same saved password.
//
// Currently I haven't implemented caching. It's only idea
// to think about in future.
passCheck(document);
}
else
{
Toast.makeText(SLogin.this, "You are not registered", Toast.LENGTH_LONG).show();
}
}
else
{
Toast.makeText(SLogin.this, "Unable to connect to database", Toast.LENGTH_LONG).show();
}
}
});
}
}
public void nUser(View v)
{
Intent intent = new Intent(SLogin.this, RegisterActivity.class);
startActivity(intent);
}
}
为获得最佳实践,请添加以下内容 class
public class PrefUtilities {
private SharedPreferences preferences;
Context context;
private PrefUtilities(Context context) {
preferences = PreferenceManager.getDefaultSharedPreferences(context);
this.context = context;
}
public static PrefUtilities with(Context context){
return new PrefUtilities(context);
}
public void setUserLogin(boolean isUserLogedin){
preferences.edit().putBoolean(context.getString(R.string.pref_key_user_status),isUserLogedin).apply();
}
public boolean isUserLogedin(){
return preferences.getBoolean(context.getString(R.string.pref_key_user_status),false);
}
}
在 onCreate
方法中检查登录状态
if(PrefUtilities.with(this).isUserLogedin()){
Intent intent = new Intent(SLogin.this, StudentCP.class);
intent.putExtra(STUDENT_EMAIL, email);
startActivity(intent);
}
在 passCheck
方法中保存登录状态
private void passCheck(@NonNull DocumentSnapshot snapshot)
{
final String uPass = tPassword.getText().toString();
final String storedPass = snapshot.getString("password");
if (storedPass != null && storedPass.equals(uPass))
{
PrefUtilities.with(this).setUserLogin(true);
Intent intent = new Intent(SLogin.this, StudentCP.class);
intent.putExtra(STUDENT_EMAIL, email);
startActivity(intent);
}
else
{
Toast.makeText(SLogin.this, "Invalid Password!", Toast.LENGTH_LONG).show();
}
}
当用户注销时使用下面的方法更改 SharedPreferences
PrefUtilities.with(this).setUserLogin(false);
您还可以在 PrefUtilities
class 保存用户邮箱中添加其他方法