用戶填寫用戶名、密碼、確認密碼、手機號完成註冊,下次啟動應用自動驗證登錄狀態。
public class UserAccount {
private String username;
private String passwordHash; // 必須存儲哈希值,不可明文
private String phone;
private long registerTime;
private boolean isLoggedIn;
// Getter/Setter + toJson/fromJson
}
public class AccountManager {
private static final String PREF_NAME = "user_account";
private static final String KEY_ACCOUNT = "account_data";
private SharedPreferences sp;
public AccountManager(Context context) {
sp = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
}
public void saveAccount(UserAccount account) {
String json = new Gson().toJson(account);
sp.edit().putString(KEY_ACCOUNT, json).apply();
}
public UserAccount getAccount() {
String json = sp.getString(KEY_ACCOUNT, null);
return json != null ? new Gson().fromJson(json, UserAccount.class) : null;
}
public void clearAccount() {
sp.edit().remove(KEY_ACCOUNT).apply();
}
public boolean isLoggedIn() {
UserAccount account = getAccount();
return account != null && account.isLoggedIn();
}
}
public void register(View view) {
String username = etUsername.getText().toString().trim();
String password = etPassword.getText().toString();
String confirm = etConfirm.getText().toString();
String phone = etPhone.getText().toString().trim();
// 1. 前端驗證
if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
showToast("用戶名/密碼不能為空");
return;
}
if (!password.equals(confirm)) {
showToast("兩次密碼不一致");
return;
}
if (!Patterns.PHONE.matcher(phone).matches()) {
showToast("手機號格式錯誤");
return;
}
// 2. 密碼加密(生產環境建議使用 BCrypt/SCrypt)
String passwordHash = hashPassword(password);
// 3. 保存賬號
UserAccount account = new UserAccount();
account.setUsername(username);
account.setPasswordHash(passwordHash);
account.setPhone(phone);
account.setRegisterTime(System.currentTimeMillis());
account.setLoggedIn(true); // 註冊即登錄
new AccountManager(this).saveAccount(account);
// 4. 跳轉主頁
startActivity(new Intent(this, MainActivity.class));
finish();
}
public class SplashActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
new Handler().postDelayed(() -> {
AccountManager am = new AccountManager(this);
if (am.isLoggedIn()) {
// 後台靜默驗證 token 有效性
verifyTokenInBackground(am.getAccount());
} else {
startActivity(new Intent(this, LoginActivity.class));
finish();
}
}, 1500); // 品牌展示時間
}
}
| 風險點 | 解決方案 |
|---|---|
| 明文存儲密碼 | 必須使用 PBKDF2/BCrypt + 隨機鹽值哈希 |
| SharedPreferences 可被 Root 讀取 | 敏感數據加密存儲(Android Keystore + AES-256) |
| 設備遷移賬號丟失 | 引入雲端同步(Firebase Auth / 自建服務) |
| 暴力破解嘗試 | 登錄失敗計數 + 指數退避鎖定機制 |