http://blog.sina.com.cn/s/blog_667ac0360102yrg1.html
多端登录系统需求
1 多端登录,用户信息只缓存一份;
2 多端登录,会话超时可以自定义;
3 多端登录,同一个账号能否支持多人同时登录,可以配置;支持账号异常,再下次登录时候提醒上次登录的IP地址,注意修改密码;
4 支持服务器重启不影响客户端的会话保持,下次操作也不会要求重新登录;
5 登录会话可以清楚显示当前用户有几个客户端在线,这些在线用户访问的是哪个系统,系统版本,APP版本;
6 多端的分类是按照客户端类型,浏览器、APP、微信、微信小程序来分类;而且还能细分到所登录的系统是哪个;比如:账号A,会话有3个,其中这3个会话有可能所登录的系统是不同的;
注意:当不允许多人同时登录的时候,一个账号是否进行了多人同时登录的判断是:用户ID+客户端平台+系统ID;
7 支持多业务系统、多客户端,多会话配置,统一登录;系统缓存的用户信息仅存储一份;
用户表——User
Integer id; // ID
Integer perId; // 人员ID
String username; // 账号
String password; // 密码(加密)
Integer status; // 状态 0 注册未激活 1 正常已激活 -1 账号锁定
String salt; // “盐”
String head; // 头像
Date createTime; // 创建时间
Date updateTime; // 更新时间
// 服务器重启后,不需要客户端重新登录;服务端使用内存、数据库两级存储,当缓存找不到,则从数据库找并且刷新缓存;
用户会话表——UserSession(与登录用户的内存对象一一映射)
Integer uid; // 用户ID(主键)
String username; // 用户账号
Date createTime; // 创建时间
Date updateTime; // 更新时间
用户会话明细表——UserSessionDetail
String uuid; // 会话ID
String ip; // 登录IP地址
Integer expireAt; // 超时时间(秒)
Integer appSys; // 所属应用系统
String uid; // 用户ID
String ver; // APP版本号 或者 H5资源版本号
Date createTime; // 创建时间
Date updateTime; // 更新时间
系统日志(登录日志、重要操作日志)——SystemLog
Integer id; // ID
Integer appSysId; // 属于哪个应用系统
Integer userId; // 用户ID
String username; // 用户名
String type; // LOGIN 登录日志,ACTION 操作日志;
String operation; // 操作内容
String ip; // 登录IP
String addr; // 登录地区
String loginPlatform; // 多端区分
String appPackage; // app包名
Date createTime; // 创建时间
Date updateTime; // 更新时间
后端系统用户缓存结构
_usContext用户会话上下文,与数据库保持同步;
key:uid, value:userSessionObj
_uuidContext
key: uuid, value: userSessionObj;value指向_usContext的userSessionObj;
系统配置
1 多端登录配置
{
“remind”: true,
“platforms”: [
{“name”: “browser”, “multiLogin”: true, “maxAge”:1800},
{“name”: “app”, “multiLogin”: false, “maxAge”:31536000},
{“name”: “wxgzh”, “multiLogin”: false, “maxAge”:31536000},
{“name”: “wxapp”, “multiLogin”: false, “maxAge”:31536000}]
}
说明
multiLogin:是否允许多人同时登录;
maxAge:会话超时时间;
2 不允许多人同时登录,是否需要提醒用户
login_remind=true // true要通知、false不通知;
不允许多人同时登录的规则
每次登录时候,检测当前平台是否允许多人同时登录;若不允许,则检测该用户是否有人已经登录过了;若已经有人登录过了,也就是多人同时登录了,后面登录的人会覆盖前面登录的人;前面登录的人下次请求时候会下线;
若login_remind=true,则前面登录的人,下次再登录的时候会给出风险提示,提示上次登录的IP、地区;
用户会话操作
1 新增
1)UserSession对象持久到数据库;
若不存在,则插入;若存在,则更新时间;
2)UserSessionDetail对象持久到数据库(包含最后一次操作时间);
若允许多端登录,则插入明细;
若不允许多端登录,则根据uid、platform、appsys查询是否有人登录过;若登录过,则覆盖前面人的uuid;否则,插入明细;
3)刷新缓存
数据库操作完毕后,查询最新的UserSession(不存在最后一次操作时间);
将最后一次操作时间,重新写入到UserSessionDetail;数据库并没有存最后一次操作时间,是存在与内存的;
将最新的UserSession放入_usContext、_uuidContext;
2 删除
1)获取数据
根据request获取uuid,根据uuid从_uuidContext获取UserSession,然后获取uid;
2)删除数据库
根据uuid从数据库删除会话明细UserSessionDetail;若该用户的会话明细都删完了,则把会话信息UserSession删除;否则,更新UserSession的updateTime更新时间;
3)删除缓存
根据uuid从缓存_uuidContext删除;
根据uid从数据库加载UserSession,若存在,则刷新;若不存在,则从缓存_usContext移除;
3 查询
1)获取数据
根据request获取uuid,根据uuid获取UserSession;
若UserSession!=null,则直接返回;否则,如下处理:
从数据库加载UserSession;若存在,刷新缓存,明细需要写入最后操作时间为当前时间(明细的最后操作时间没有写入数据库,因为是为了避免频繁更新当前用户的最后操作时间);若不存在,则返回null;
注意:新增、删除、查询时候从数据库加载数据的时候,这些操作都需要线程同步;
登录操作
1) 验证账号、密码正确;
2)根据request获取platform、appSys、ip,从配置文件获取isMultiLogin、maxAge,生成uuid;
3)创建会话信息UserSession以及UserSessionDetail,并调用上方的用户会话的新增操作;
4)设置cookie
设置uuid、platform到cookie,超时时间就是maxAge;
5)返回登录成功;
退出操作
1)调用上面定义的用户会话清除操作
2)删除cookie
删除uuid、platform;
3)重定位到登录页面
登录拦截器
根据request,调用上面的获取UserSession操作
若存在,则验证是否会话是否超时;若超时,则返回账号超时(ajax)、重定位到登录页(html);若未超时,则继续执行;
若不存在,则返回账号超时(ajax)、重定位到登录页(html);
参考