14-用户登录详解
一、为什么需要登录
用户登陆APP之后,能够使用服务器的一些服务,存储一些信息。比如说在商城应用中,用户只有登陆以后才能添加订单,这设计到API权限验证的问题,之后会说明。
二、APP登陆流程简介
1、从输入框获取用户名以及密码,判断两者的合法性。比如“密码长度必须大于5”,等。
2、密码加密:在通信过程中为了密码的安全性,对密码进行加密。比如:对称加密,服务器与APP之间有一个一致的加密(解密)密钥。
3、使用POST方式,携带用户名和密码向服务器请求,不使用GET方式,因为GET方式是在URL后面直接跟随信息的,易被截取。
4、登录成功之后,保存服务器返回的信息,该Activity设置setResult(),向启动这个Activity的Activity返回信息(返回码)。返回原先的Activity之后,设置用户头像,用户名等信息。
下面给出示例代码:
/**
* 登录
*/
private void login() {
String phone = etxt_phone.getText().toString().trim();
String pwd = etxt_pwd.getText().toString().trim();
if (TextUtils.isEmpty(phone)) {
ToastUtils.showToast(this, R.string.phone_empty);
return;
}
if (phone.length() != 11) {
ToastUtils.showToast(this, R.string.phone_error);
return;
}
if (TextUtils.isEmpty(pwd)) {
ToastUtils.showToast(this, R.string.pwd_empty);
return;
}
if (pwd.length() < 5) {
ToastUtils.showToast(this, R.string.pwd_short);
return;
}
Map<String, String> params = new HashMap<>(2);
params.put(Constants.PHONE, phone);
params.put(Constants.PASSWORD, DESUtil.encode(Constants.DES_KEY, pwd));
mHttpHelper.post(Constants.URL_LOGIN, params, new BaseCallback<LoginRespMsg>() {
@Override
public void onRequestBefore() {
}
@Override
public void onFailure(Request request, Exception e) {
}
@Override
public void onSuccess(Response response, LoginRespMsg userLoginRespMsg) {
if (userLoginRespMsg.getStatus() == BaseRespMsg.STATUS_SUCCESS) {
//登录成功,保存用户信息,token
CNApplication.saveLoginMsg(userLoginRespMsg);
ToastUtils.showToast(LoginActivity.this, R.string.login_success);
setResult(Constants.RESULT_LOGIN_SUCCESS);
LoginActivity.this.finish();
} else {
ToastUtils.showToast(LoginActivity.this, R.string.login_failed);
}
}
@Override
public void onError(Response response, int errorCode, Exception e) {
}
});
}
三、服务器返回的数据详解
第二点中提到“保存服务器返回的信息”,那么我们有必要去了解这个信息的含义,之后再谈为什么需要保存。
服务器通过JSON格式返回我们的数据有:
states:int类型,用于告诉APP是否登录成功。
message:String类型,用于说明states的含义。
user:对象类型,保存用户头像地址,用户名等信息。
token:String类型,作用是APP权限验证密钥。token是一个比较重要的字符串,当APP登录成功得到token以后,下一次去请求一些比较重要的服务器URL(比如添加订单)的时候,需要把这个参数携带上,由于服务器返回的token值有一定的期效(比如7天),只有携带有效的token去请求这个URL才能成功。这就是所谓的API权限验证。当token过期了就应该提示用户重新登录。
校验失败的http返回码:
401:token 丢失
402:token 错误
403: token 过期
此外token还有可能错误、丢失。因此,我们在调用服务器的关键API的时候除了要把token带上以外,在回调的时候如果token有问题的话也需要进行回调,并且跳到登录页面请求用户重新登录。因此就有必要对OKHttpHelper以及相关的CallBack进行改写,在所有get 和post方法中都添加token,回调中也添加token相关的回调。示例代码如下:
//get方法中在URL后面追加token参数
LoginRespMsg msg = CNApplication.getLoginMsg();
if (msg != null) {
String token = msg.getToken();
if (!TextUtils.isEmpty(token)) {
if (url.indexOf("?") > 0) {
url = url + "&" + TOKEN + "=" + token;
} else {
url = url + "?" + TOKEN + "=" + token;
}
}
}
//post方法中添加token参数
LoginRespMsg msg = CNApplication.getLoginMsg();
if (msg != null) {
String token = msg.getToken();
if (!TextUtils.isEmpty(token)) {
builder.add(TOKEN, msg.getToken());
}
}
/**
* token有错误的时候回调,一般是跳到登录页面
* @param response
* @param code
*/
public abstract void onTokenProblem(Response response, int code);
四、如何保存服务器返回的信息
由于这些数据是全局的,因此我在自定义Application中添加如下两个静态方法来存储服务器返回的信息。主要的思想就是把信息重新转换为JSON格式,然后存放到SharePreference里面。由于Application的全局性,APP启动的时候(Application初始化)就可以判断用户是否已经登录,并且进行一些业务逻辑的处理,例如没有登录的话就跳转到登录页面,已经登录就直接跳到首页。
public static void saveLoginMsg(LoginRespMsg userLoginRespMsg) {
String json = JSONUtil.toJSON(userLoginRespMsg);
PreferencesUtils.putString(mCtx, Constants.LOGIN_MSG, json);
}
public static LoginRespMsg getLoginMsg() {
LoginRespMsg msg = JSONUtil.fromJson(PreferencesUtils.getString(mCtx, Constants.LOGIN_MSG), LoginRespMsg.class);
return msg;
}
五、登录时机&退出登录
登录时机:
1、一般APP在第一次使用的时候最好直接跳到登录Activity,登陆完成后进入主界面。
2、在请求关键API的时候返回token错误、丢失、过期的时候也需要跳到登录Activity。
退出登录实现:
1、如果服务端有提供退出登录的API的话,就应该先去调用,清除服务端的登录信息。
2、清除本地的token信息。
3、然后刷新头像、用户名、我的收藏等信息。
六、登录拦截
当用户进行需要登录的操作,比如下订单的时候,如果用户没有登录,那么应该先跳到登录页面,然后跳到原先需要去跳转的页面,比如下订单页面。简单滴举个例子说,用户如果需要下单,如果没有登录,跳到登录页面登陆完毕以后直接进入订单页面而不是停留在原先的页面,这样可以大大提高用户体验。
实现这个功能的核心思想是预先把原来的Intent保存起来,登陆完毕以后再利用这个Intent进行跳转。
示例代码:
我在自定义Application增加如下方法以及参数:
private static Intent mIntent;
public static void putIntent(Intent intent) {
mIntent = intent;
}
public static Intent getmIntent() {
return mIntent;
}
/**
* 跳到原先的页面
*/
public static void junpToActivity(Context context) {
context.startActivity(mIntent);
mIntent = null;
}
然后自定义跳转方法:
/**
* 自定义跳转,是否需要登录
*
* @param intent
* @param isNeedLogin
*/
public void startActivity(Intent intent, boolean isNeedLogin) {
if (isNeedLogin) {
if (CNApplication.isLogined()) {
startActivity(intent);
} else {
//保存Intent
CNApplication.putIntent(intent);
Intent loginIntent = new Intent(this, LoginActivity.class);
startActivity(loginIntent);
}
} else {
startActivity(intent);
}
}
在登录页面进行返回
if (CNApplication.getmIntent() != null) {
CNApplication.junpToActivity(LoginActivity.this);
LoginActivity.this.finish();
}
如果觉得我的文字对你有所帮助的话,欢迎关注我的公众号:
公众号:Android开发进阶我的群欢迎大家进来探讨各种技术与非技术的话题,有兴趣的朋友们加我私人微信huannan88,我拉你进群交(♂)流(♀)。