其实所说的被挤下线功能,就是一个账号在A客户端保持登陆状态,然后又在B客户端进行了登陆操作,那么A客户端就会被顶下线
很多伙伴在开发自己公司产品的时候,一般都会考虑用户账号安全,或者用户账号功能限制等问题,这时候就要考虑到单点登陆的功能
App如何知道该账户已经在其他设备上登陆了呢?有三种实现方式
服务端需要返回 Token,每次在app登录时为 app 分配一个新的 token,如果在某次请求中 app 传递 token不是最新的,则视为需要重新登录,在token失效的情况下,返回约定好的code
使用第三方的监听器。比如集成了环信,环信自身有提供连接状态的接听,通过监听环信的用户状态,从而达到监听app自身用户系统的效果
我们的项目中集成了环信的即时聊天,所以就使用了环信的监听器监听用户状态,用来判断是否已在其他地方登陆,实现挤下线功能。
具体实现:
1.首先在初始化环信的时候设置一个全局的监听器里面注册一个连接监听
// 注册连接监听
EMChatManager.getInstance()
.addConnectionListener(connectionListener);
2.实现这个连接监听的那个检测到连接断开的时候是用户被移除还是连接冲突即账号在其他地方登陆,做出相应的操作
// create the global connection listener
connectionListener = new EMConnectionListener() {
@Override
public void onDisconnected(int error) {
if (error == EMError.USER_REMOVED) {
onCurrentAccountRemoved();
} else if (error == EMError.CONNECTION_CONFLICT) {
onConnectionConflict();
}
}
@Override
public void onConnected() {
// in case group and contact were already synced, we supposed to
// notify sdk we are ready to receive the events
}
};
3.我们关心账号在别处登陆,这个时候,我们一般要条红钻到MainActivity,然后强制弹出对话框提示用户重新登陆
/**
* 账号在别的设备登录
*/
protected void onConnectionConflict() {
Intent intent = new Intent(appContext, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Constant.ACCOUNT_CONFLICT, true);
appContext.startActivity(intent);
}
这个地方检测到登陆冲突之后需要回到MainActivity,并为MainActivity携带了一个标识和一个标记位Intent.FLAG_ACTIVITY_TASK,表示在一个新的task中开启一个Activity,如果包含这个Activity的task已经在运行,那么这个Activity就回到前台显示,然后回调onNewIntent()方法处理这个Intent
4.回到MainActivity中的onNewIntent()方法
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent.getBooleanExtra(Constant.ACCOUNT_CONFLICT, false) && !isConflictDialogShow) {
showConflictDialog();
} else if (intent.getBooleanExtra(Constant.ACCOUNT_REMOVED, false)
&& !isAccountRemovedDialogShow) {
showAccountRemovedDialog();
}
}
首先会判断表示,如果时账户冲突就会提出对话框提示用户跳转登陆页面重新登陆,另外这个对话框是不能取消也不可关闭的
这样被顶下线功能就基本实现了
1.创建一个token
首先我们要在服务端创建一个token的值,这个值是和userid以及手机号码绑定到一起的,也就是请求token的时候我们要给服务端传递当前设备以及用户id。token这个值就是客户端调用服务端的凭证
android中获取唯一标识 deviceid
//deviceId
public static String getDeviceId(Context context) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (tm == null) {
return "";
}
return "" + tm.getDeviceId();
}
但是这个很多机器是无法获取到的这个值,所以后来改为mac地址,mac地址很好理解,就是用户上网的上网许可证,这个买手机的时候,你翻开电池就知道里面有一个标签!!
/**
* 获取MAC地址,注意:手机重启,mac地址为null;
*
* @param context
* @return mac地址;
*/
public static String getMac(Context context) {
if (context != null) {
WifiManager wifi = (WifiManager) context
.getSystemService(Context.WIFI_SERVICE);
WifiInfo info = wifi.getConnectionInfo();
return info.getMacAddress();
} else {
return "";
}
}
2.获取token
app进入时要先获取token,获取token的时候我们要给服务端传递当前设备的设备号,当我们换了设备后设备号变了,返回的token值也就变了,那么原设备的token相对来说就失效了,当在原设备和服务端有交互的时候会返回token失效,用户登陆注销要及时更新token值
3.请求交互携带token
每次客户端和服务端有任何交互的时候都要传递这个token参数,当我们在另一台设备登陆的时候原设备上存的token就失效了,因为数据库里当前用户的token已经在新设备获取token值的覆盖了,这样原设备请求时就回提示token值失效了
4.根据服务端返回码处理逻辑
事先客户端和服务端要约定好一个code码,咧如:我们约定返回码为99的时候代表token失效,那么当我们请求服务端返回值为99的时候就要提示您的登陆转台失效,请您重新登陆
switch (code) {
case 1:
break;
case 99://被踢下线
//Do Something
break;
}
上面说了自己用自己的业务系统实现app单点的基本流程