FineReport:任意时刻只允许在一个客户端登陆账号的插件
在使用FineReport报表系统中,处于账户安全考虑,有些企业希望同一账号在任意时刻智能在统一客户端登录。那么当A用户在C1客户端登陆后,该账号又在另外一个C2客户端登陆,服务器如何取判断呢?
开发原理
当服务器在得知A在C1登陆后,在cookie里面写入一个标识ID~将浏览器标记,然后以后的访问自然就能够根据匹配用户名和对应的标记来确定这个用户是不是在换浏览器登陆了,当匹配到用户异地登陆,就要把之前已经登陆的用户先登出,再登陆新请求的用户。当然关闭页面事件里要向后台先发送一个请求,后台要记得清除改用户标记的缓存。
那么客户端怎么知道自己的账号在异地登陆了呢?
这个就要基于心跳了~因为我们的http不是长连接的,所以只能模拟了,弄一个轮询ajax不断的问服务器,我是否在异地登陆,因为之前服务器任何一个账号登陆都会又一个ID标识,那么当接收到一个客户端心跳时,我们只要拿出里面的ID和用户名跟保存的匹配~匹配到存在该用户名,但是ID不对,那说明一定是另外一个客户端登陆了这个账号了,这个时候就告知客户端,你的账号已经异地登陆,然后前端提示刷新就可以了。
如何实现?
这里要用到FineReport提供的接口,RequestInterceptor
接口内容
package com.fr.stable.fun;
import com.fr.stable.fun.mark.Layer;
import com.fr.stable.fun.mark.Mutable;
import com.fr.stable.web.RequestCMDReceiver;
/**
* Created by richie on 16/8/9.
* 请求拦截器,通过传递op和cmd进行内置请求的拦截
*/
public interface RequestInterceptor extends Mutable, RequestCMDReceiver, Layer {
String MARK_STRING = "RequestInterceptor";
int CURRENT_LEVEL = 1;
}
相关引用类
package com.fr.stable.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Created by richie on 16/8/9.
* 请求接收器
*/
public interface RequestCMDReceiver {
/**
* cmd参数值
* @return cmd参数值
*/
String getCMD();
/**
* 执行
* @param req http请求
* @param res http应答
* @param sessionID 会话ID
* @throws Exception 处理失败则抛出异常
*/
void actionCMD(HttpServletRequest req, HttpServletResponse res,
String sessionID) throws Exception;
/**
* 执行请求
* @param req http请求
* @param res http响应
* @throws Exception 处理失败则抛出异常
*/
void actionCMD(HttpServletRequest req, HttpServletResponse res) throws Exception;
}
注册方式
<extra-core> <RequestInterceptor class="com.fr.plugin.xxx.youclassname" op="fs_load" cmd="login" pid="com.fr.plugin.xxx.name"/> </extra-core>
其中pid的值应该和插件的id值一致,通过这样的注册方式,就可以使用自己定义的处理逻辑来覆盖掉默认的登录验证请求。
以上,通过故意制造报错的方式我们能够看到~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)这个登陆成功之后需要做一些上面说的操作~
下面是两个代码片段,主要就是处理登陆标记和登出清除的.
片段1
@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);
}
片段2
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));
}
}gf =
下面就是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;
}
}
});
};
FineReport:任意时刻只允许在一个客户端登陆账号的插件的更多相关文章
- php实现同一时间内一个账户只允许在一个终端登陆
在账户表的基础上,我新建了一个账户account_session表,用来记录登录账户的account_id和最新一次登录成功用户的session_id,然后首先要修改登录方法:每次登录成功后,要将登录 ...
- 简单php实现同一时间内一个账户只允许在一个终端登陆
在账户表的基础上,我新建了一个账户account_session表,用来记录登录账户的account_id和最新一次登录成功用户的session_id,然后首先要修改登录方法:每次登录成功后,要将登录 ...
- 【MySQL8】 安装后的简单配置(主要解决navicat等客户端登陆报错问题)
一.navicat等客户端登陆报错的原因 使用mysql,多数我们还是喜欢用可视化的客户端登陆管理的,个人比较喜欢用navicat.一般装好服务器以后,习惯建一个远程的登陆帐号,在mysql8服务器上 ...
- RAC 10.2.0.5,客户端登陆间断遭遇ORA-12545
实验环境: 服务端:OEL 5.7 + Oracle 10.2.0.5 RAC 客户端:Windows 7 + Oracle 11.2.0.1 Client 1.客户端登陆间断遭遇ORA-125 ...
- Projecet客户端登陆无法通过验证
客户反映使用Project客户端登陆project服务器的时候,只有域管理员账户才能够登陆成功,其他的账户登陆都无法验证通过,无论是https的方式还是客户端的方式,但是域账户登陆计算机是可以登陆成功 ...
- Android 实现两个list分别出现(在某一时刻只出现一个控件)
第一种方法: 在.xml文件中将这两个List分别放入不同的布局管理器中,比如说 <RelativeLayout android:layout_width="match_parent& ...
- 修改TortoiseSVN客户端登陆用户
TortoiseSVN是一款常用且非常不错的SVN工具,俗称小乌龟.开发的时候,经常用的当然是TortoiseSVN客户端了. 一般情况下,TortoiseSVN服务器提供的IP地址和用户都不会变,而 ...
- Oracle之配置客户端登陆多个远程数据库
一.引言 一直搞不明白Oracle数据库的客户端是怎么回事,怎么配置,前几天由于工作中需要用到Oracle,而且需要连接两个不同的数据库,就通过上网和请教同事终于把客户端的配置搞定了,记录之,学习之 ...
- java在线聊天项目 客户端登陆窗口LoginDialog的注册用户功能 修改注册逻辑 增空用户名密码的反馈 增加showMessageDialog()提示框
LoginDialog类的代码修改如下: package com.swift.frame; import java.awt.EventQueue; import java.awt.event.Acti ...
随机推荐
- 在Openfire上弄一个简单的推送系统
推送系统 说是推送系统有点大,其实就是一个消息广播功能吧.作用其实也就是由服务端接收到消息然后推送到订阅的客户端. 思路 对于推送最关键的是服务端向客户端发送数据,客户端向服务端订阅自己想要的消息.这 ...
- Kooboo CMS技术文档之五:站点配置管理
站点关系 管理站点间的关系,站点可以有子站点,子站点继承父站点的部分配置数据,同时子站点还可以根据需要,本地化由父站点继承而来的数据.通过继承和本地化,可以让子站点在用最小的改动代价,来完成一个与父站 ...
- 调用AJAX做登陆和注册
先建立一个页面来检测一下我们建立的用户名能不能用,看一下有没有已经存在的用户名吗 可以通过ajax提示一下 $("#uid").blur(function(){ //取用户名 va ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(72)-微信公众平台开发-消息处理
系列目录 前言 Senparc.Weixin.MP SDK提供了MessageHandler消息处理类 在作者的Wiki中也详细说明了如何定义这个类,下面我们来演示,消息的回复,及效果 了解Messa ...
- ASP.NET WebApi OWIN 实现 OAuth 2.0
OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...
- 【云知道】LoadRunner 录制问题集锦
关键词:各路录制小白汇集于此 虽然知道君对录制不感冒,但总是看到扎堆的人说这些问题,忍不住要站出来了. 百度虽好,帮助了很多小白,但关键是百度并没有排除错误内容,经过历史的几年传播,错的都快变对的了, ...
- C# BackgroundWorker 详解
在C#程序中,经常会有一些耗时较长的CPU密集型运算,如果直接在 UI 线程执行这样的运算就会出现UI不响应的问题.解决这类问题的主要途径是使用多线程,启动一个后台线程,把运算操作放在这个后台线程中完 ...
- BI分析受阻?FineBI推出SPA螺旋式分析新功能!
过去,企业级的数据分析通常会有这么几种场景,业务部门托信息部门分析数据,结果报表一出,唇枪舌剑争论你我高低,数据不准,指标不对.信息部门欠缺业务概念,业务部门不懂技术逻辑,数据分析之路,暂时搁浅. 后 ...
- TCP/IP之Nagle算法与40ms延迟
Nagle算法是针对网络上存在的微小分组可能会在广域网上造成拥塞而设计的.该算法要求一个TCP连接上最多只能有一个未被确认的未完成的小分组,在该分组确认到达之前不能发送其他的小分组.同时,TCP收集这 ...
- Selenium-java-获取当前时间
1 获取当前时间 // 获取当前时分秒 Calendar now = Calendar.getInstance(); int is = now.get(Calendar.HOUR_OF_DAY); i ...