如果用户通过同一账号在不同浏览器甚至不同ip登录会对账号安全,数据逻辑处理产生巨大隐患,这里介绍几种常见的解决办法:
1.MySql数据库设置登录状态
实现:
在account表中添加字段'isLogin'
登录时,更新为true
退出时,更新为false
当登录时查询该字段,如果为true,就表明该账号已登录,阻止这次登录
缺点:
- 没有设置登录过期时间,账号一直处在登录状态十分不安全
- 当用户非正常退出,如直接关闭浏览页、设备断电,isLogin无法得到更新,始终处在true状态,导致用户永远无法登录该账号
解决办法:
- 新加字段'loginTime',登录的同时检查loginTime,如果与现在时间大于设定的值,则即使isLogin为true仍可登录(解决用户非正常退出后,无法再次登录的问题),登录成功后将loginTime设置为当前时间
但是如果我们设置的间隔时间太短,上一个用户还没退出,下一个用户就开始登录,因为我们设置了loginTime,第二个用户也可以成功登录账号,这就又绕回来了,根本没有解决问题
如果我们设置的间隔时间过长,用户非正常退出后,如果想重新登录就得等待完这个间隔时间,比如很多网站账号登录的安全最效时间是30分钟,如果不小心退出来,则至多要等30分钟,对用户简直是种折磨
这就引入第二种解决办法
2.数据库的临时表
mysql中是支持临时表的,临时表顾名思义,就是使用时创建,关闭时自动删除的一种表,一般有着缓存数据,提高数据提取效率的功能
实现:
连接数据库时(即登录成功时)创建一个临时表,将账号等信息写进去
登录时检查对应账号的临时表是否存在,如果是,就证明有用户已经登录,阻止这次登录,没有则允许登录
重点来了:临时表的生存是由是否连接数据库决定的,当断开数据库的连接时,临时表会自动删除,这就解决了第一个方法产生的问题,当用户非正常退出时,也就意味着数据库的断开,直接实现了用户退出即清空数据的需求
缺点:
- 持续连接数据库会对用户和服务器都造成巨大的性能和流量浪费,占用服务器资源,导致服务器阻塞,影响服务器运行
- 创建表和查询表相对于查询字段耗时更大,尤其是服务器用户流量增加时,数据库压力极大
- 对于php等脚本语言,一个脚本结束后是会自动回收内存的,这就意味着,当登录的脚本/接口结束后,数据库自动会断开连接,临时表会被删除,下一个用户仍然可以登录,又又绕回来了
3.令牌Token的使用
实现:
在登录时创建一个随机值token,写入对应账号的字段'token'中,并将该值返回前端,当每次调用get或post方法时,将token随数据一起提交
提交后,后端比对该值是否与字段'token'的值是否相同,相同则继续执行下面的代码...不相同就证明有另一个用户登录该账号了
缺点:
- 每次提交请求都要携带该token,对于十分复杂的前端和后端交互,不利于代码的编写
- 每次请求都要连接数据库,对于频繁的请求,会造成数据库压力巨大
解决办法:
- 对于第二个缺点,可以采用redis数据库,这是一个基于内存的数据库,读写速度十分高效
以上的方法是网上常见但无法根本解决该文章提出的问题或性能开支巨大的方案,这里提供一个不成熟的方案
最终的解决办法:
4.公钥和私钥的使用
实现:
在数据库账号表中增加一个字段‘privateKey’来储存私钥
用户登录成功后,后端生成一个新的公钥和私钥,将私钥储存在session中(这里可能有人会问,同一个浏览器多开窗口session不是会共用吗?不要着急,一会会解释),将公钥返回前端。
"sha512", "private_key_bits" => 4096, "private_key_type" => OPENSSL_KEYTYPE_RSA,);$res = openssl_pkey_new($config); //提取私钥openssl_pkey_export($res, $private_key); //生成公钥$public_key = openssl_pkey_get_details($res);$public_key = $public_key["key"];session_start();//保存私钥到session中$_SESSION['privateKey'] = $private_key;//返回公钥exit($public_key);
在前端中,所有提交的数据都采用该公钥加密
var publicKey;//获取后端生成的公钥$.ajax({ url:'getPublicKey.php', dataType:'json', async:false, success:function(response){ publicKey = response['publicKey']; if(!publicKey){ console.log('获取失败'); } }})//创建加密对象var encryptor = new JSEncrypt()//设置公钥encryptor.setPublicKey(publicKey)//对数据加密后发送$.ajax({ url:'../postData.php', type:'post', dataType:'json', data:encryptor.encrypt('你要发送的数据'),//对发送的数据进行加密 success:function(){ ... }
在后端中,利用session保存下的私钥解密
当其他用户登录时,会更新session和数据库对应账号的私钥privateKey,更新之后,第一个用户加密后的数据肯定是用当前的私钥解密不了的(一般情况下会返回空值),如果解密失败了,就证明私钥和公钥不对应,说明有另外的用户登录了,将此用户踢出即可
在上面的代码中添加
if(!$content){ exit('已有其他用户登录,你无法对该账号进行操作'); //进行踢出用户等操作...}
优点:
- 登录后不用每次请求都连接数据库来判断是否有其他用户登录,对数据库性能要求低
- 采用了RSA加密,即使没有SSL,也保证了数据的安全
- 只需在前端写一个加密方法,在后端写解密方法,不用单独为每个请求设置token,对前端和后端极其友好
缺点:
- 每次都对数据进行加密解密,直接响应时间有所增加
- rsa加密有可能无法加密过大文件
- 后端返回的公钥有可能会被中途拦截,从而利用该公钥加密的数据进行提交
应对措施:
- 牺牲数据安全性,采用对称加密,加密和解密速度会大大提升,但密钥使用非对称加密传输到前端
- 将大文件分开传输(这不废话吗?哪有大文件不分开传的)
- 在前端构建私钥和公钥,将公钥发送至后端,在后端用该公钥对后端生成的公钥加密后返回前端,在前端使用私钥解密
这种方案可以针对同一浏览器同时多开浏览页的情况,如果不想限制同一浏览器,在账号表中加入‘ip’字段,登录时检查该字段,具体实现不做解释了
该文章为作者原创,转载请获取作者同意
来源地址:https://blog.csdn.net/weixin_50338358/article/details/128843053