php 异地登录和唯一登录的检测和实现逻辑 - 附简单扫码登录实现逻辑
异地登录
概念解释
指网站或 APP 用户在不同地点登录到系统的行为。判断异地一般会使用用户登录 IP 地址作为参考标准,对于 APP 可以通过接入移动数据网络的位置定位到具体城市,所以 APP 采用的判定更加符合异地的定义。但对于网站来说,IP 地址判断是最简单也最可行对的方案了。
检测和实现逻辑
主要实现逻辑
- 用户每次登录后添加一条登录记录,存放 IP 地址、登录时间等信息(没有做登录记录的,也可以将 IP 信息直接更新到用户表记录中)
- 在登录用户的每次请求中(校验过用户登录状态后),检测当前登录 IP 是否与上次登录 IP 一致,根据后台配置决定是否结束当前的登录状态。如果后台配置禁止异地登录,立即结束当前的登录状态,并在前端提示用户存在异地登录情况,当前登录状态失效,需要重新登录,提示用户非本人登录须修改登录密码
- 『非必要』用户可以查看不同 IP 地址的登录记录,选择禁止某些 IP 登录
- 『非必要』用户每次登录后(账号密码已通过验证),检测当前登录 IP 是否与上次登录 IP 一致,不一致,返回前端添加额外的验证码,增加上一条的禁用 IP 登录判断
主要涉及到的表
用户登录记录表(或者用户表)、用户登录禁用 IP 记录表(非必要)
唯一登录
概念解释
指网站或 APP 用户只允许存在唯一一个登录状态。唯一登录不强调异地,只要有新的登录状态,旧的登录状态就会被关闭。用户的登录状态一般是保存在 session 中的,所以要实现唯一登录就相当于唯一 session。
检测和实现逻辑
主要实现逻辑
- 用户每次登录后根据用户名添加一条(或更新)session 记录,保存用户名、session ID、登录设备(设备类型)、登录 IP、登录时间等信息
- 在登录用户的每次请求中(校验过用户登录状态后),检测当前用户 session ID 是否与上次登录用户的 session ID 一致,根据后台配置决定是否结束当前的登录状态。如果后台配置唯一登录,立即结束当前的登录状态,并在前端提示系统设置用户唯一登录,当前用户已在其他地方登录,当前登录状态失效,需要重新登录,提示用户非本人登录须修改登录密码
- 『非必要』可以将用户名和登录设备作为主键,这样可以将判断条件放宽到允许单个用户在不同设备上唯一登录。如果不需要这一步,session 记录表中的登录设备字段也可以去掉
- 『非必要』存在登录设备,用户自然就可以管理登录设备。可以选择禁止某些设备登录,然后在用户每次登录后判断当前登录设备是否被禁止。
[notice]因为 web 端无法获取设备号和设备名称,而设备类型判断比较比较粗犷(Android,iOS,Mac,Windows...),所以如果存在多部相同类型的手机(如苹果手机),也只能登录一部[/notice]
[notice]不同设备的唯一登录参考了微信账号与安全中的登录设备管理,可能因为微信属于是 APP,所以可以获取到的设备信息更加丰富一些。[/notice]
主要涉及的表
用户登录 session 记录表
扩展内容1 - 单点登录 SSO
在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
定义取自百度百科,之前做外包项目一直就没碰到过这个需求,但大概了解单点登录的概念。
SSO 主要功能
- 所有应用系统共享一个身份认证系统
统一的认证系统是SSO的前提之一。认证系统的主要功能是将用户的登录信息和用户信息库相比较,对用户进行登录认证;认证成功后,认证系统应该生成统一的认证标志(ticket),返还给用户。另外,认证系统还应该对 ticket 进行效验,判断其有效性。
- 所有应用系统能够识别和提取 ticket 信息
要实现 SSO 的功能,让用户只登录一次,就必须让应用系统能够识别已经登录过的用户。应用系统应该能对 ticket 进行识别和提取,通过与认证系统的通讯,能自动判断当前用户是否登录过,从而完成单点登录的功能。
概念比较复杂,实现也比较困难。我的理解,就像是公交卡系统,本来苏州有苏州的公交卡系统,盐城有盐城的公交卡系统,不同城市之间各自为营,不能互通。如果我经常在苏州和盐城来往(没车,只能坐公交 OωO ),一种方法就是我买两张公交卡,一张苏州的,一张盐城的。但这样的话,我就需要同时维持两个公交卡的使用。还有一张方法就是买一张江苏省通用的公交卡(要苏州和盐城的公交卡系统都能够识别),这样就方便了很多,起码钱包里能节省点空间 ヾ(≧∇≦*)ゝ 。公家卡通用要做很多事情,首先这张公家卡要能够提供有效并且安全的认证信息,证明是省内通用的,然后所有支持的城市都要升级公家卡系统,以支持这张公交卡能够被识别。
SSO 当然会对用户提供一些便利,登录信息也能共享。但就跟一句俗语说的一样,不要把所有的鸡蛋都放在一个篮子里,如果认证系统崩溃了,那么所有只支持 SSO 的应用系统都会崩溃。
扩展内容2 - 扫码登录
扫码登录主要用于存在多个客户端的场景。在一些支持 APP 的网站上,使用 APP 扫描网站上的二维码,在 APP 端授权登录,即可让网站跳过输入用户名、密码的相对来说繁琐且不太安全的登录方式,直接登录。
之前做过一个公会的 APP,有一个管理后台,在后台管理系统上,添加了一个扫码登录的登录方式。当时参考了网上的一些方案,选择了最简单的方法实现。
管理后台页面每次切换到扫码登录页面,会在用户表中更新授权登录的随机字符串(其实可以存放在 redis 之类的缓存中),根据随机字符串生成一个登录二维码返回给前端,二维码里面存放的是随机字符串加密后的 hash 值。前端做一个定时器,定时询问后台当前会话是否已授权登录。并且二维码会定时过期,刷新后会重新请求二维码,后端生成新的随机字符串,并生成二维码返回前端,这点比较像验证码的刷新机制。当用户通过 APP 调用扫码并确认授权登录后,会将二维码中获取的加密的 hash 值通过接口传递到后端。后端解密之后取出当前 APP 登录用户的随机字符串与之比对,如果相同则修改用户授权登录状态为允许。当前端定时询问在后端得到允许后,后端为当前会话添加登录状态,并修改授权登录状态为不允许,同时更新随机字符串,等待下一次的请求,前端登录成功。
基本的逻辑是这样的,当然为了安全和用户体验,还有很多可以优化的点。比如说二维码的过期可以放在轮询请求中,一过期,就更新随机字符串,当前端提交刷新请求时,生成二维码返回就行。前端可以获取一些当前需要授权登录设备的信息,比如说 IP 地址和设备类型,将这些信息以 hash 值作为主键存储好,在扫码后先去获取授权登录的设备及 IP 地址信息,并展示在 APP 端提供授权参考依据,之后确认授权登录,再去修改授权登录状态。