一个账号在a电脑登陆了,此时在b电脑登陆,就会将a踢下线,需要解决两个问题:
- 一、确保账号只能在一个地方登陆;
- 二、登陆后发送消息通知;
对于第一个问题,我们可以借助于session存储于redis之后,实现session在多个站点,多台服务器共享的情况下,统一通过session_id来管理用户的session数据,
下面是一段session存储于redis的代码并且展示了通过session_id去redis获取session数据的例子。
$redis_config = ['host' => '192.168.200.229', 'port' => 6379];
@ini_set("session.save_handler", "redis");
@ini_set("session.save_path", 'tcp://'.$redis_config['host'].':'.$redis_config['port']);
session_start();
$_SESSION['user_id'] = 123;
$_SESSION['user_name'] = 'helin';
$_SESSION['email'] = 'xxxxxx@163.com';
$session_id = session_id();
echo $session_id;
//ssr242gfioap1auk0njtdg07q4
echo "<br>";
$redis = new redis();
$redis->connect($redis_config['host'], $redis_config['port']);
// redis 用 session_id 作为 key 并且是以 string 的形式存储
echo $redis->get('PHPREDIS_SESSION:' . $session_id);
//user_id|i:123;user_name|s:5:"helin";email|s:14:"xxxxxx@163.com";
echo "<br>";
echo json_encode($_SESSION);
//{"user_id":123,"user_name":"helin","email":"xxxxxx@163.com"}
exit;
有了上面代码的实现,我们可以思考一下具体实现的逻辑:
- 步骤一:用户登陆的时候把session_id保存到用户数据表,然后根据uid来生成一个键,将一些用户信息保存在redis里面(生成一个登陆标识及session_id等);
- 步骤二:当另外一个终端用同一账号登陆的时候,获取到用户表的session_id。并做步骤一的操作;
- 步骤三:通过步骤二获取到的session_id就变成是旧的信息了,有可能是当前在用的,也有可能是过期的,此时可以先根据这个session_id从redis清除掉里面的数据,不管它是不是有效的;
通过上面的处理后,若在另外一个终端有登陆,那此时其它地方的session应该是被清除掉了,再访问需要登陆的页面的时候肯定是退出状态;那么第一个问题就算是解决了,现在来讨论第二个问题:
如果实时性要求不高,而且也不需要考虑大数据的压力,例如是一个ERP管理系统,公司内部用的,登陆的人一般不会太多,可以通过ajax定时请求轮询方式实现,这样会简单很多,实现形式就是通过服务器判断当前的session数据是否还存在,若不存在了,根据之前保存在redis里面的登陆状态数据及用户信息,查出此用户表里面保存的session_id跟redis里面保存的session_id是否一致,如果是登陆状态并且不一致,那就可以提示用户已经在另外的终端登陆。如果是session_id一致,那应该可以肯定是过期了。此时可以清除掉用户相关的redis数据然后做重定向登陆。
实时性要求高的,或者性能上面有考虑,可以使用socket的方式,与ajax方式不一样的是它可以实时把数据推送到客户端,这种用户体验最好,可控性强,就是实现起来稍微比第一种方式要复杂一点点,可以借助socket.io + node.js的方式实现。
除了上面的方式,我们也可以换个思路,做最简单的处理,当用户在另外的终端登陆,当前可以直接退出,不做提示,当前如果需要做其它操作的时候,再去判断登陆问题也是可以的,只不过没有那么实时,还有就是在规划之初就要考虑到这个流程怎么走用户体验会好一些。