问题传送门---》》》http://bbs.fanruan.com/thread-83526-1-1.html
先来分析这个问题:
当A用户在C1客户端登陆后,该账号又在另外一个C2客户端登陆,服务器如何取判断呢?
很简单
当服务器在得知A在C1登陆后,在cookie里面写入一个标识ID~将浏览器标记,然后以后的访问自然就能够根据匹配用户名和对应的标记来确定这个用户是不是在换浏览器登陆了,当匹配到用户异地登陆,就要把之前已经登陆的用户先登出,再登陆新请求的用户。当然关闭页面事件里要向后台先发送一个请求,后台要记得清除改用户标记的缓存。
那么客户端怎么知道自己的账号在异地登陆了呢?
这个就要基于心跳了~因为我们的http不是长连接的,所以只能模拟了,弄一个轮询ajax不断的问服务器,我是否在异地登陆,因为之前服务器任何一个账号登陆都会又一个ID标识,那么当接收到一个客户端心跳时,我们只要拿出里面的ID和用户名跟保存的匹配~匹配到存在该用户名,但是ID不对,那说明一定是另外一个客户端登陆了这个账号了,这个时候就告知客户端,你的账号已经异地登陆。然后前端提示刷新就可以了
原理就是这个原理,那么怎么实现呢?
这里要用到FR提供的接口
RequestInterceptor ---》http://doc.finedevelop.com/pages/viewpage.action?pageId=1048886
通过故意制造报错的方式我们能够看到~FR登陆请求都是继承于
com.fr.fs.web.service.FSLoadLoginAction 这个类的~、
进一步反编译JAR可以看到~这个类是继承于
com.fr.web.core.ActionNoSessionCMD 最后实现 ActionCMD, RequestInterceptor
那么正好,我们的插件主类就可以免去很多自己写,直接继承于FSLoadLoginAction就可以用来处理所有的自定义登陆请求
【凡是需要在登陆时做得事情都可以在这里做】
当然actionCMD(HttpServletRequest req, HttpServletResponse res)这个执行方法还是要重写的~
还有就是protected void signOnSuccess(HttpServletRequest req, HttpServletResponse res, PrintWriter writer, String url)这个登陆成功之后需要做一些上面说的操作~
下面是两个代码片段,主要就是处理登陆标记和登出清除的
- @Override
- public void actionCMD(HttpServletRequest req, HttpServletResponse res)
- throws Exception {
- String username = WebUtils.getHTTPRequestParameter(req, Constants.FR_USERNAME);
- String heartBeat = WebUtils.getHTTPRequestParameter(req, "__heartbeat__");
- if(ComparatorUtils.equals(heartBeat, "__active__")){
- if(StringUtils.isEmpty(username)){
- username = WebUtils.getHTTPRequestParameter(req, "__username__");
- if(!StringUtils.isEmpty(username)){
- req.getSession(true).removeAttribute("__username__");
- }
- }
- //如果用户名不为空且已登录的列表中不包含该用户名说明已经被踢下线
- if(!StringUtils.isEmpty(username) && !log.containsKey(username)){
- writeResult(res,false);
- return ;
- }
- //如果在已登录的列表中找到了该用户名的记录,但是ID不匹配也说明被踢下线了
- if(log.containsKey(username)){
- String crtUUID = WebUtils.getHTTPRequestParameter(req, "_sessionid_");
- SingleLoginBean logBean = log.get(username);
- String oldId = logBean.getId();
- if(!ComparatorUtils.equals(crtUUID,oldId)){
- writeResult(res,false);
- return;
- }else{
- //将当前时刻设置为最近活跃时刻
- logBean.setWait4removeTime(new Date().getTime());
- }
- }
- writeResult(res,true);
- //登出太久不活跃的用户 30S以上
- checkAllUser();
- return;
- }
- super.actionCMD(req, res);
- }
复制代码- protected void signOnSuccess(HttpServletRequest req, HttpServletResponse res, PrintWriter writer, String url) throws IOException, JSONException {
- String username = WebUtils.getHTTPRequestParameter(req, Constants.FR_USERNAME);
- String uuid = req.getSession(true).getId();
- SingleLoginBean logBean = new SingleLoginBean(uuid,req,res,req.getSession(true));
- logBean.setWait4removeTime(new Date().getTime());
- //后面的用户登录成功后需要先将旧的用户转移到等待删除的列表中
- remove4logout(req);
- //将新登录的用户添加到已经登录的用户中
- log.put(username, logBean);
- if ("true".equals(WebUtils.getHTTPRequestParameter(req, ParameterConsts.__REDIRECT__))) {
- res.sendRedirect(url);
- } else {
- writer.print(JSONObject.create().put("url", url));
- }
- }
复制代码 下面就是JS轮询了
- var askServer4Active = function(){
- var sessionid = getCrtSessionid();
- if( sessionid == "" || sessionid == null ){
- return ;
- }
- var url = FR.servletURL+"?op=fs_load&cmd=login&__heartbeat__=__active__&_sessionid_="+sessionid;
- FR.ajax({
- url: url,
- type: "POST",
- dataType:"JSON",
- success: function(msg){
- if(!msg.success){
- if(active){
- active = false;
- clearInterval(timer);
- FR.Msg.alert("警告","您的账号已在其他客户端登陆!\n如非本人授权,请及时修改密码!\n3秒后页面将跳转至登陆页!");
- setTimeout(function(){
- document.location = FR.servletURL+"?op=fs";
- },3000);
- }
- }else{
- active = true;
- }
- }
- });
- };
复制代码 逻辑大概就是这么个样子~插件在帖子里面直接下载就可以了~
|