最近一个android小程序需要登录功能,我简单实现了一下。现在记录下来也当做个笔记,同时也希望可以相互学习。所以,如果我的代码有问题,还各位请提出来。多谢了!
下面,就简述一下此实例的主要内容:
输入用户名和密码 ,从本地文件userinfo.json中读取users。判断此用户名是否在users中,如果不在则加入users,每次退出Activity都使用AES算法加密users,然后保存到userinfo.json中。用户名下拉菜单是由PopupWindow + ListView 实现。
运行效果图:



主要的代码:
1、用户类User
01.
package com.example.logindemo;
02.
03.
import org.json.JSONException;
04.
import org.json.JSONObject;
05.
import android.util.Log;
06.
07.
public class User {
08.
private String mId;
09.
private String mPwd;
10.
private static final String masterPass<A class=keylink href="http://www.it165.net/edu/ebg/" target=_blank>word</A> = "FORYOU"; // AES加密算法的种子
11.
private static final String JSON_ID = "user_id";
12.
private static final String JSON_PWD = "user_pwd";
13.
private static final String TAG = "User";
14.
15.
public User(String id, String pwd) {
16.
this.mId = id;
17.
this.mPwd = pwd;
18.
}
19.
20.
public User(JSONObject json) throws Exception {
21.
if (json.has(JSON_ID)) {
22.
String id = json.getString(JSON_ID);
23.
String pwd = json.getString(JSON_PWD);
24.
// 解密后存放
25.
mId = AESUtils.decrypt(masterPass<A class=keylink href="http://www.it165.net/edu/ebg/" target=_blank>word</A>, id);
26.
mPwd = AESUtils.decrypt(masterPassword, pwd);
27.
}
28.
}
29.
30.
public JSONObject toJSON() throws Exception {
31.
// 使用AES加密算法加密后保存
32.
String id = AESUtils.encrypt(masterPassword, mId);
33.
String pwd = AESUtils.encrypt(masterPassword, mPwd);
34.
Log.i(TAG, "加密后:" + id + " " + pwd);
35.
JSONObject json = new JSONObject();
36.
try {
37.
json.put(JSON_ID, id);
38.
json.put(JSON_PWD, pwd);
39.
} catch (JSONException e) {
40.
e.printStackTrace();
41.
}
42.
return json;
43.
}
44.
45.
public String getId() {
46.
return mId;
47.
}
48.
49.
public String getPwd() {
50.
return mPwd;
51.
}
52.
}
2、保存和加载本地User列表
01.
package com.example.logindemo;
02.
03.
import java.io.BufferedReader;
04.
import java.io.FileInputStream;
05.
import java.io.FileNotFoundException;
06.
import java.io.IOException;
07.
import java.io.InputStreamReader;
08.
import java.io.OutputStream;
09.
import java.io.OutputStreamWriter;
10.
import java.io.Writer;
11.
import java.util.ArrayList;
12.
import org.json.JSONArray;
13.
import org.json.JSONException;
14.
import org.json.JSONTokener;
15.
16.
import android.content.Context;
17.
import android.util.Log;
18.
19.
public class Utils {
20.
21.
private static final String FILENAME = "userinfo.json"; // 用户保存文件名
22.
private static final String TAG = "Utils";
23.
24.
/* 保存用户登录信息列表 */
25.
public static void saveUserList(Context context, ArrayList<User> users)
26.
throws Exception {
27.
/* 保存 */
28.
Log.i(TAG, "正在保存");
29.
Writer writer = null;
30.
OutputStream out = null;
31.
JSONArray array = new JSONArray();
32.
for (User user : users) {
33.
array.put(user.toJSON());
34.
}
35.
try {
36.
out = context.openFileOutput(FILENAME, Context.MODE_PRIVATE); // 覆盖
37.
writer = new OutputStreamWriter(out);
38.
Log.i(TAG, "json的值:" + array.toString());
39.
writer.write(array.toString());
40.
} finally {
41.
if (writer != null)
42.
writer.close();
43.
}
44.
45.
}
46.
47.
/* 获取用户登录信息列表 */
48.
public static ArrayList<User> getUserList(Context context) {
49.
/* 加载 */
50.
FileInputStream in = null;
51.
ArrayList<User> users = new ArrayList<User>();
52.
try {
53.
54.
in = context.openFileInput(FILENAME);
55.
BufferedReader reader = new BufferedReader(
56.
new InputStreamReader(in));
57.
StringBuilder jsonString = new StringBuilder();
58.
JSONArray jsonArray = new JSONArray();
59.
String line;
60.
while ((line = reader.readLine()) != null) {
61.
jsonString.append(line);
62.
}
63.
Log.i(TAG, jsonString.toString());
64.
jsonArray = (JSONArray) new JSONTokener(jsonString.toString())
65.
.nextValue(); // 把字符串转换成JSONArray对象
66.
for (int i = 0; i < jsonArray.length(); i++) {
67.
User user = new User(jsonArray.getJSONObject(i));
68.
users.add(user);
69.
}
70.
71.
} catch (FileNotFoundException e) {
72.
e.printStackTrace();
73.
} catch (IOException e) {
74.
e.printStackTrace();
75.
} catch (JSONException e) {
76.
e.printStackTrace();
77.
} catch (Exception e) {
78.
e.printStackTrace();
79.
}
80.
81.
return users;
82.
}
83.
}
3、AES加密/解密
01.
package com.example.logindemo;
02.
03.
04.
import java.security.SecureRandom;
05.
06.
import javax.crypto.Cipher;
07.
import javax.crypto.KeyGenerator;
08.
import javax.crypto.SecretKey;
09.
import javax.crypto.spec.IvParameterSpec;
10.
import javax.crypto.spec.SecretKeySpec;
11.
12.
public class AESUtils {
13.
public static String encrypt(String seed, String cleartext)
14.
throws Exception {
15.
byte[] rawKey = getRawKey(seed.getBytes());
16.
byte[] result = encrypt(rawKey, cleartext.getBytes());
17.
return toHex(result);
18.
}
19.
20.
public static String decrypt(String seed, String encrypted)
21.
throws Exception {
22.
byte[] rawKey = getRawKey(seed.getBytes());
23.
byte[] enc = toByte(encrypted);
24.
byte[] result = decrypt(rawKey, enc);
25.
return new String(result);
26.
}
27.
28.
private static byte[] getRawKey(byte[] seed) throws Exception {
29.
KeyGenerator kgen = KeyGenerator.getInstance("AES");
30.
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
31.
sr.setSeed(seed);
32.
kgen.init(128, sr);
33.
SecretKey skey = kgen.generateKey();
34.
byte[] raw = skey.getEncoded();
35.
return raw;
36.
}
37.
38.
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
39.
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
40.
Cipher cipher = Cipher.getInstance("AES");
41.
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(
42.
new byte[cipher.getBlockSize()]));
43.
byte[] encrypted = cipher.doFinal(clear);
44.
return encrypted;
45.
}
46.
47.
private static byte[] decrypt(byte[] raw, byte[] encrypted)
48.
throws Exception {
49.
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
50.
Cipher cipher = Cipher.getInstance("AES");
51.
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(
52.
new byte[cipher.getBlockSize()]));
53.
byte[] decrypted = cipher.doFinal(encrypted);
54.
return decrypted;
55.
}
56.
57.
private static String toHex(String txt) {
58.
return toHex(txt.getBytes());
59.
}
60.
61.
private static String fromHex(String hex) {
62.
return new String(toByte(hex));
63.
}
64.
65.
private static byte[] toByte(String hexString) {
66.
int len = hexString.length() / 2;
67.
byte[] result = new byte[len];
68.
for (int i = 0; i < len; i++)
69.
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2),
70.
16).byteValue();
71.
return result;
72.
}
73.
74.
private static String toHex(byte[] buf) {
75.
if (buf == null)
76.
return "";
77.
StringBuffer result = new StringBuffer(2 * buf.length);
78.
for (int i = 0; i < buf.length; i++) {
79.
appendHex(result, buf[i]);
80.
}
81.
return result.toString();
82.
}
83.
84.
private final static String HEX = "0123456789ABCDEF";
85.
86.
private static void appendHex(StringBuffer sb, byte b) {
87.
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
88.
}
89.
}
4、LoginActivity.java
001.
package com.example.logindemo;
002.
003.
import java.util.ArrayList;
004.
005.
import android.app.Activity;
006.
import android.app.Dialog;
007.
import android.graphics.drawable.ColorDrawable;
008.
import android.os.Bundle;
009.
import android.text.Editable;
010.
import android.text.TextWatcher;
011.
import android.util.DisplayMetrics;
012.
import android.util.Log;
013.
import android.view.View;
014.
import android.view.ViewGroup;
015.
import android.view.Window;
016.
import android.view.WindowManager;
017.
import android.view.View.OnClickListener;
018.
import android.view.ViewGroup.LayoutParams;
019.
import android.view.animation.Animation;
020.
import android.view.animation.AnimationUtils;
021.
import android.widget.AdapterView;
022.
import android.widget.AdapterView.OnItemClickListener;
023.
import android.widget.ArrayAdapter;
024.
import android.widget.Button;
025.
import android.widget.EditText;
026.
import android.widget.ImageView;
027.
import android.widget.LinearLayout;
028.
import android.widget.ListView;
029.
import android.widget.PopupWindow;
030.
import android.widget.PopupWindow.OnDismissListener;
031.
import android.widget.TextView;
032.
import android.widget.Toast;
033.
034.
public class LoginActivity extends Activity implements OnClickListener,
035.
OnItemClickListener, OnDismissListener {
036.
protected static final String TAG = "LoginActivity";
037.
private LinearLayout mLoginLinearLayout; // 登录内容的容器
038.
private LinearLayout mUserIdLinearLayout; // 将下拉弹出窗口在此容器下方显示
039.
private Animation mTranslate; // 位移动画
040.
private Dialog mLoginingDlg; // 显示正在登录的Dialog
041.
private EditText mIdEditText; // 登录ID编辑框
042.
private EditText mPwdEditText; // 登录密码编辑框
043.
private ImageView mMoreUser; // 下拉图标
044.
private Button mLoginButton; // 登录按钮
045.
private ImageView mLoginMoreUserView; // 弹出下拉弹出窗的按钮
046.
private String mIdString;
047.
private String mPwdString;
048.
private ArrayList<User> mUsers; // 用户列表
049.
private ListView mUserIdListView; // 下拉弹出窗显示的ListView对象
050.
private MyAapter mAdapter; // ListView的监听器
051.
private PopupWindow mPop; // 下拉弹出窗
052.
053.
@Override
054.
public void onCreate(Bundle savedInstanceState) {
055.
super.onCreate(savedInstanceState);
056.
setContentView(R.layout.activity_login);
057.
initView();
058.
setListener();
059.
mLoginLinearLayout.startAnimation(mTranslate); // Y轴水平移动
060.
061.
/* 获取已经保存好的用户密码 */
062.
mUsers = Utils.getUserList(LoginActivity.this);
063.
064.
if (mUsers.size() > 0) {
065.
/* 将列表中的第一个user显示在编辑框 */
066.
mIdEditText.setText(mUsers.get(0).getId());
067.
mPwdEditText.setText(mUsers.get(0).getPwd());
068.
}
069.
070.
LinearLayout parent = (LinearLayout) getLayoutInflater().inflate(
071.
R.layout.userifo_listview, null);
072.
mUserIdListView = (ListView) parent.findViewById(android.R.id.list);
073.
parent.removeView(mUserIdListView); // 必须脱离父子关系,不然会报错
074.
mUserIdListView.setOnItemClickListener(this); // 设置点击事
075.
mAdapter = new MyAapter(mUsers);
076.
mUserIdListView.setAdapter(mAdapter);
077.
078.
}
079.
080.
/* ListView的适配器 */
081.
class MyAapter extends ArrayAdapter<User> {
082.
083.
public MyAapter(ArrayList<User> users) {
084.
super(LoginActivity.this, 0, users);
085.
}
086.
087.
public View getView(final int position, View convertView,
088.
ViewGroup parent) {
089.
if (convertView == null) {
090.
convertView = getLayoutInflater().inflate(
091.
R.layout.listview_item, null);
092.
}
093.
094.
TextView userIdText = (TextView) convertView
095.
.findViewById(R.id.listview_userid);
096.
userIdText.setText(getItem(position).getId());
097.
098.
ImageView deleteUser = (ImageView) convertView
099.
.findViewById(R.id.login_delete_user);
100.
deleteUser.setOnClickListener(new OnClickListener() {
101.
// 点击删除deleteUser时,在mUsers中删除选中的元素
102.
@Override
103.
public void onClick(View v) {
104.
105.
if (getItem(position).getId().equals(mIdString)) {
106.
// 如果要删除的用户Id和Id编辑框当前值相等,则清空
107.
mIdString = "";
108.
mPwdString = "";
109.
mIdEditText.setText(mIdString);
110.
mPwdEditText.setText(mPwdString);
111.
}
112.
mUsers.remove(getItem(position));
113.
mAdapter.notifyDataSetChanged(); // 更新ListView
114.
}
115.
});
116.
return convertView;
117.
}
118.
119.
}
120.
121.
private void setListener() {
122.
mIdEditText.addTextChangedListener(new TextWatcher() {
123.
124.
public void onTextChanged(CharSequence s, int start, int before,
125.
int count) {
126.
mIdString = s.toString();
127.
}
128.
129.
public void beforeTextChanged(CharSequence s, int start, int count,
130.
int after) {
131.
}
132.
133.
public void afterTextChanged(Editable s) {
134.
}
135.
});
136.
mPwdEditText.addTextChangedListener(new TextWatcher() {
137.
138.
public void onTextChanged(CharSequence s, int start, int before,
139.
int count) {
140.
mPwdString = s.toString();
141.
}
142.
143.
public void beforeTextChanged(CharSequence s, int start, int count,
144.
int after) {
145.
}
146.
147.
public void afterTextChanged(Editable s) {
148.
}
149.
});
150.
mLoginButton.setOnClickListener(this);
151.
mLoginMoreUserView.setOnClickListener(this);
152.
}
153.
154.
private void initView() {
155.
mIdEditText = (EditText) findViewById(R.id.login_edtId);
156.
mPwdEditText = (EditText) findViewById(R.id.login_edtPwd);
157.
mMoreUser = (ImageView) findViewById(R.id.login_more_user);
158.
mLoginButton = (Button) findViewById(R.id.login_btnLogin);
159.
mLoginMoreUserView = (ImageView) findViewById(R.id.login_more_user);
160.
mLoginLinearLayout = (LinearLayout) findViewById(R.id.login_linearLayout);
161.
mUserIdLinearLayout = (LinearLayout) findViewById(R.id.userId_LinearLayout);
162.
mTranslate = AnimationUtils.loadAnimation(this, R.anim.my_translate); // 初始化动画对象
163.
initLoginingDlg();
164.
}
165.
166.
public void initPop() {
167.
int width = mUserIdLinearLayout.getWidth() - 4;
168.
int height = LayoutParams.WRAP_CONTENT;
169.
mPop = new PopupWindow(mUserIdListView, width, height, true);
170.
mPop.setOnDismissListener(this);// 设置弹出窗口消失时监听器
171.
172.
// 注意要加这句代码,点击弹出窗口其它区域才会让窗口消失
173.
mPop.setBackgroundDrawable(new ColorDrawable(0xffffffff));
174.
175.
}
176.
177.
/* 初始化正在登录对话框 */
178.
private void initLoginingDlg() {
179.
180.
mLoginingDlg = new Dialog(this, R.style.loginingDlg);
181.
mLoginingDlg.setContentView(R.layout.logining_dlg);
182.
183.
Window window = mLoginingDlg.getWindow();
184.
WindowManager.LayoutParams params = window.getAttributes();
185.
// 获取和mLoginingDlg关联的当前窗口的属性,从而设置它在屏幕中显示的位置
186.
187.
// 获取屏幕的高宽
188.
DisplayMetrics dm = new DisplayMetrics();
189.
getWindowManager().getDefaultDisplay().getMetrics(dm);
190.
int cxScreen = dm.widthPixels;
191.
int cyScreen = dm.heightPixels;
192.
193.
int height = (int) getResources().getDimension(
194.
R.dimen.loginingdlg_height);// 高42dp
195.
int lrMargin = (int) getResources().getDimension(
196.
R.dimen.loginingdlg_lr_margin); // 左右边沿10dp
197.
int topMargin = (int) getResources().getDimension(
198.
R.dimen.loginingdlg_top_margin); // 上沿20dp
199.
200.
params.y = (-(cyScreen - height) / 2) + topMargin; // -199
201.
/* 对话框默认位置在屏幕中心,所以x,y表示此控件到"屏幕中心"的偏移量 */
202.
203.
params.width = cxScreen;
204.
params.height = height;
205.
// width,height表示mLoginingDlg的实际大小
206.
207.
mLoginingDlg.setCanceledOnTouchOutside(true); // 设置点击Dialog外部任意区域关闭Dialog
208.
}
209.
210.
/* 显示正在登录对话框 */
211.
private void showLoginingDlg() {
212.
if (mLoginingDlg != null)
213.
mLoginingDlg.show();
214.
}
215.
216.
/* 关闭正在登录对话框 */
217.
private void closeLoginingDlg() {
218.
if (mLoginingDlg != null && mLoginingDlg.isShowing())
219.
mLoginingDlg.dismiss();
220.
}
221.
222.
@Override
223.
public void onClick(View v) {
224.
switch (v.getId()) {
225.
case R.id.login_btnLogin:
226.
// 启动登录
227.
showLoginingDlg(); // 显示"正在登录"对话框,因为此Demo没有登录到web服务器,所以效果可能看不出.可以结合情况使用
228.
Log.i(TAG, mIdString + " " + mPwdString);
229.
if (mIdString == null || mIdString.equals("")) { // 账号为空时
230.
Toast.makeText(LoginActivity.this, "请输入账号", Toast.LENGTH_SHORT)
231.
.show();
232.
} else if (mPwdString == null || mPwdString.equals("")) {// 密码为空时
233.
Toast.makeText(LoginActivity.this, "请输入密码", Toast.LENGTH_SHORT)
234.
.show();
235.
} else {// 账号和密码都不为空时
236.
boolean mIsSave = true;
237.
try {
238.
Log.i(TAG, "保存用户列表");
239.
for (User user : mUsers) { // 判断本地文档是否有此ID用户
240.
if (user.getId().equals(mIdString)) {
241.
mIsSave = false;
242.
break;
243.
}
244.
}
245.
if (mIsSave) { // 将新用户加入users
246.
User user = new User(mIdString, mPwdString);
247.
mUsers.add(user);
248.
}
249.
250.
} catch (Exception e) {
251.
e.printStackTrace();
252.
}
253.
closeLoginingDlg();// 关闭对话框
254.
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
255.
finish();
256.
}
257.
break;
258.
case R.id.login_more_user: // 当点击下拉栏
259.
if (mPop == null) {
260.
initPop();
261.
}
262.
if (!mPop.isShowing() && mUsers.size() > 0) {
263.
// Log.i(TAG, "切换为角向上图标");
264.
mMoreUser.setImageResource(R.drawable.login_more_down); // 切换图标
265.
mPop.showAsDropDown(mUserIdLinearLayout, 2, 1); // 显示弹出窗口
266.
}
267.
break;
268.
default:
269.
break;
270.
}
271.
272.
}
273.
274.
@Override
275.
public void onItemClick(AdapterView<?> parent, View view, int position,
276.
long id) {
277.
mIdEditText.setText(mUsers.get(position).getId());
278.
mPwdEditText.setText(mUsers.get(position).getPwd());
279.
mPop.dismiss();
280.
}
281.
282.
/* PopupWindow对象dismiss时的事件 */
283.
@Override
284.
public void onDismiss() {
285.
// Log.i(TAG, "切换为角向下图标");
286.
mMoreUser.setImageResource(R.drawable.login_more_up);
287.
}
288.
289.
/* 退出此Activity时保存users */
290.
@Override
291.
public void onPause() {
292.
super.onPause();
293.
try {
294.
Utils.saveUserList(LoginActivity.this, mUsers);
295.
} catch (Exception e) {
296.
e.printStackTrace();
297.
}
298.
}
299.
300.
}
其他一些布局和资源配置我就不详细列出了,想看的可以下载 http://www.it165.net/uploadfile/files/2014/1031/LoginDemo.zip
本文介绍了一个Android小程序的登录功能实现,包括用户管理、本地数据存储与加密、登录流程设计与优化等内容。通过使用AES加密算法保护用户数据安全,并实现用户登录信息的保存与加载,确保了用户体验与数据安全之间的平衡。
643

被折叠的 条评论
为什么被折叠?



