Java-实现异地登陆和超时登陆
2019独角兽企业重金招聘Python工程师标准>>>
一、原理
1. 异地登陆
同一个账号,在不同的电脑(也可以不同的浏览器)登陆系统,前一个已经登陆的账号session被销毁,用户进行下一步操作时跳转错误页面。
2. 超时登陆
登陆后无操作*分钟后自动销毁session,用户进行下一步操作时跳转错误页面。
3. 区分
异地登陆和超时登陆起效时跳转的错误页面不相同。
二、实现
1. 超时登陆
由系统控制,在web.xml中配置,或者由监听器控制,利用session.setMaxInactiveInterval(interval);方法控制,本次主要展示在web.xml中配置的方法,如下:
<session-config><session-timeout>30</session-timeout><!-- 单位:分钟 -->
</session-config>
2. 异地登陆
首先在Controller中判断登陆用户的账号密码是否正确,通过以后判断session是不是异地登陆(过程在监听器中判断,此处判断监听器处理后的返回值),如果是,session销毁,如下:
@RequestMapping("/loginCheck")
@ResponseBody
public Map<String, Object> loginCheck(HttpServletRequest request, String userLoginNumber, String userLoginPasswd) {Map<String, Object> ret = new HashMap<String, Object>(2);// 存储错误信息的Map容器String errorMsg = "";// 错误信息HttpSession session = request.getSession();/*代码块,判断账号,并给errorMsg赋值或不赋值(正确)*/if (/* 账户判断成功,没有错误信息 */) {/* 监听器实现HttpSessionListener和HttpSessionAttributeListener接口。监听器private static一个Map类型的变量。当监听到对Attribute操作时(登陆验证成功向session添加用户数据,一般都会用到),进入HttpSessionAttributeListener.attributeAdded()方法,向公共map变量放入数据。当监听到session销毁操作时,进入HttpSessionListener.sessionDestroyed()方法,将公共map里的值移除。LoginListenner.isLogonUser()就是得到map中存储的值*/HttpSession isLoginSession = LoginListenner.isLogonUser(userLoginNumber);if (null != isLoginSession) {// 如果没有,则当前session是一个新的session,之前的session已经销毁// 异地登陆: 在监听器中区分超时和异地登陆, 在拦截器中判断isLoginSession.setAttribute("sessionDestroyedStatus", "busy");isLoginSession.invalidate();// 表示异地登陆,销毁session}ret.put("result", "1");}return ret;
}
监听器实现(原理在上面),包括区分异地登陆和超时登陆需要跳转不同页面的处理,过程如下:
import java.util.HashMap;
import java.util.Map;import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import com.nielsen.sfa.common.Constants;
import com.nielsen.sfa.model.User;
import com.nielsen.sfa.utils.CommonUtil;/* @Description: 登录监听类-处理同一时间只允许账号,单地点登录*/
public class LoginListenner implements HttpSessionListener,HttpSessionAttributeListener {/** * 用于存放账号和session对应关系的map */ private static Map<String, HttpSession> map = new HashMap<String, HttpSession>(); Logger log = LoggerFactory.getLogger(LoginListenner.class);public void sessionDestroyed(HttpSessionEvent se) {// 如果session销毁, 则从map中移除这个用户try {HttpSession session = se.getSession();System.err.println(session);
// mobileLoginUser.remove(se.getSession());User user = (User) se.getSession().getAttribute(Constants.USER_INFO);if(null != user && StringUtils.isNotBlank(user.getUserLoginNumber()))map.remove(user.getUserLoginNumber());/*在这里做区分异地登陆和超时登陆跳转不同的错误页面的区分:超时登陆:由系统自动销毁,没有标识符异地登陆:手动销毁,销毁前用setAttribute()标识这个session销毁原因是异地登陆注意:如果是另外有手动调用session.invalidate(),也需要注明,如下手动退出然后,因为session是要销毁的,这里我们用一个公共类的公共变量Map存储标识符,然后去拦截器中判断,并控制跳转错误页面。*/// 判断session是怎么被销毁的, 并存入Map给拦截器判断(区分超时登录和异地登陆)if (null == se.getSession().getAttribute("sessionDestroyedStatus")) {// 超时自动销毁CommonUtil.sessionStatusMap.remove("sessionDestroyedStatus");CommonUtil.sessionStatusMap.put("sessionDestroyedStatus", "timeout");} else if (se.getSession().getAttribute("sessionDestroyedStatus").equals("busy")) {// 异地登陆时setAttributeCommonUtil.sessionStatusMap.remove("sessionDestroyedStatus");CommonUtil.sessionStatusMap.put("sessionDestroyedStatus", "busy");} else if (se.getSession().getAttribute("sessionDestroyedStatus").equals("logout")) {// 手动退出CommonUtil.sessionStatusMap.remove("sessionDestroyedStatus");CommonUtil.sessionStatusMap.put("sessionDestroyedStatus", "logout");}}catch (Exception e) {log.error(e.getLocalizedMessage(),e);}}/** * 当向session中放入数据触发 */ public void attributeAdded(HttpSessionBindingEvent event) { String name = event.getName(); if (name.equals(Constants.USER_INFO)) { User user = (User) event.getValue();
// if (map.get(Constants.USER_INFO) != null) {
// HttpSession session = map.get(user.getUserLoginNumber());
// session.removeAttribute(user.getUserLoginNumber());
// session.invalidate();
// } map.put(user.getUserLoginNumber(), event.getSession()); } } /** * 当向session中移除数据触发 */ public void attributeRemoved(HttpSessionBindingEvent event) { String name = event.getName(); if (name.equals(Constants.USER_INFO)) { User user = (User) event.getValue(); map.remove(user.getUserLoginNumber()); } } public void attributeReplaced(HttpSessionBindingEvent event) { } public static HttpSession isLogonUser(String loginNumber) {return map.get(loginNumber);}@Overridepublic void sessionCreated(HttpSessionEvent se) {}}
然后我们在springMVC的拦截器中实现(拦截器配置这里就不写了):
import java.io.PrintWriter;import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import com.nielsen.sfa.model.User;
import com.nielsen.sfa.utils.CommonUtil;public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String contextPath = request.getServletContext().getContextPath();HttpSession session = request.getSession();User user=(User)session.getAttribute("userInfo");/*在监听器中处理好的map,在这里判断,跳转对应的错误页面注意:跳转前需要把公共map中的值remove掉,否则会报错*/if (user == null || StringUtils.isEmpty(user.getUserLoginNumber())) {/*这里单独处理拦截AJAX请求的原因:session销毁后如用户正在进行AJAX操作中(打开AJAX页面或控件部件,只差一个提交的情况)可以正常操作*/// 拦截AJAX请求if (request.getHeader("X-Requested-With") != null && request.getHeader("X-Requested-With").equalsIgnoreCase("XMLHttpRequest")) {response.setCharacterEncoding("UTF-8");response.setContentType("text/html");// 判断session销毁的状态,设置header,在前端ajax error中判断if (null == CommonUtil.sessionStatusMap.get("sessionDestroyedStatus")|| CommonUtil.sessionStatusMap.get("sessionDestroyedStatus").equals("timeout")) {// session超时过期CommonUtil.sessionStatusMap.remove("sessionDestroyedStatus");response.setHeader("sessionStatus", "timeout");return false;} else if (CommonUtil.sessionStatusMap.get("sessionDestroyedStatus").equals("busy")) {// 异地登陆CommonUtil.sessionStatusMap.remove("sessionDestroyedStatus");response.setHeader("sessionStatus", "busy");return false;}}// "sessionDestroyedStatus"在loginController中设置if (null == CommonUtil.sessionStatusMap.get("sessionDestroyedStatus")|| CommonUtil.sessionStatusMap.get("sessionDestroyedStatus").equals("timeout")) {// session超时过期CommonUtil.sessionStatusMap.remove("sessionDestroyedStatus");String requestStatus = request.getParameter("requestStatus");/*文件上传也会拦截不了,所以手动在前端设置标识符*/if (requestStatus != null && requestStatus.equals("uploadFile")) {// 如果该请求是文件上传response.setCharacterEncoding("UTF-8");response.setContentType("text/html");PrintWriter out = null;out = response.getWriter();out.print("timeout");out.flush();} else {response.sendRedirect(contextPath + "/jsp/login.jsp");}return false;} else if (CommonUtil.sessionStatusMap.get("sessionDestroyedStatus").equals("busy")) {// 异地登陆CommonUtil.sessionStatusMap.remove("sessionDestroyedStatus");response.sendRedirect(contextPath + "/jsp/abnormal/notAuthorized.jsp"); return false;}} return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion");}}
3. 区分
这里的区分主要是跳转区分,就是根据存储在内存中的公共map标识符,在拦截器中判断跳转,代码如上。
三、建议
百度了一圈发现大家都建议对session的attribute进行操作而不建议销毁整个session,因为会遇到如下的几个坑,还有后期维护不方便,还好我写了很多注释,感觉不加注释以后别人要理解好久。
1. 说一下遇到的坑
因为session调用销毁方法,已经销毁了,你无法判断这个session有没有被销毁,null != session可不行,因为session销毁的原理是:“里面的值被清空,就是Attribute清空,然后这个session对象还在的,但是只是把isValid属性设置为false,并且对象不能被获取了,session id不变,除非你又创建了新的session”。
就是当我们要手动销毁session时,外面无法判断session有没有被销毁,用null != session判断显然是不行的,用session.isNew()和request.isRequestedSessionIDValid()判断,经测试也是不行的,当session已经被销毁,再进行session.invalidate()等对session进行操作的方法操作时,就会报错“session already invalidate”。
而最直观的isValid属性只能在DEBUG模式下看到,而不能直接调用判断,就是这一点比较坑,目前我在百度找的还没有什么解决办法,希望我是抛砖引玉,有大神可以解答,或者我以后找到了解决方案会随时更新。
转载于:https://my.oschina.net/NamiZone/blog/871542
Java-实现异地登陆和超时登陆相关推荐
- java 实现登录超时,Java-实现异地登陆和超时登陆
一.原理 1. 异地登陆 同一个账号,在不同的电脑(也可以不同的浏览器)登陆系统,前一个已经登陆的账号session被销毁,用户进行下一步操作时跳转错误页面. 2. 超时登陆 登陆后无操作*分钟后自动 ...
- java 验证登陆_Java的登陆验证问题
java中的登陆验证问题可以有多种方式进行验证,通过拦截器功能完成,可以通过过滤器功能完成,也可以简单的代码在JSP页面中单独完成,其中都 涉及到一个关键的验证步骤,这个验证原理ASP,PHP,JAV ...
- ❤️Java实现模拟QQ(消息通信+登陆界面美化)❤️
一.登陆界面的实现 登陆界面主要使用了JFrame,以及相关的一些组件,并且在界面中加上监听 登陆界面效果图 登陆界面代码Login类 package com.lding.login;import c ...
- 测试案例设计-账户名登陆、QQ登陆、测试水杯
目录 账户名和密码登陆测试 QQ登陆的测试用例 百度云盘APP核心功能需求分析 账户名和密码登陆测试 首先根据登陆这个需求的流程来分析:输入账户名.密码.验证码的测试点 输入正确的账户名.密码.验证码 ...
- php同个用户同时只能登陆一个, 后登陆者踢掉前登陆者
php同个用户同时只能登陆一个, 后登陆者踢掉前登陆者php同个用户同时只能登陆一个, 后登陆者踢掉前登陆者 通常用户登陆,如果没有特别的限定, 同一个用户可以同时登陆, 今天搞了一个东西限定一个用户 ...
- SSH安全登陆原理:密码登陆与公钥登陆
SSH全称(Secure SHell)是一种安全的应用层网络协议,用于计算机间的安全通信,是目前一套成熟的远程登陆解决方案. 有两种方法登陆: 密码登陆 公钥登陆 密码登陆 1.客户端填写用户名密码发 ...
- qq登陆inc.php,登陆验证 qq登陆验证 php 登陆验证
用户登录验证脚本,Chkpwd.asp '=======用户登录验证脚本======= '如果尚未定义Passed对象,则将其定义为false,表示没有通过验证 If IsEmpty(Session( ...
- 【ubuntu20.04】安装百度输入法和搜狗输入法之后,有登陆界面,登陆之后黑屏,只有鼠标可以移动,点击无效,长时间等待提示“连接失败,请注销后重试”
参考文章 https://blog.csdn.net/hgtjcxy/article/details/90645838 步骤如下: ctrl+alt+F4 进入命令行的界面,备注:F2-F7 自己尝试 ...
- joomla QQ登录,微信登录,微博登陆,人人登陆,明道登录的第三方登录
在joomla网站上集成QQ,微信,支付宝,人人,明道,短信,微博登录现在已经成为可能.只需要安装ZMAX程序人开发的ZMAX第三方登录组件,一键就可以让你的网站轻松集成当前流行的第三方登录. 国产开 ...
- dede后台登陆又返回登陆界面怎么办
dede后台登陆又返回登陆界面......网上说的DATA等文件夹都有写入权限,试过好几种方法都不行! 其实最有可能的原因就是data文件夹的权限问题.大家可以通过FTP看下data文件的权限是否是7 ...
最新文章
- C++中的参数传递方式:传值、传地址、传引用总结
- join为什么每个字符都分割了 js_js的join()与 split() (转)
- 273 Integer to English Words 整数转换英文表示
- 北京大学AI写作机器人来了,会替代记者?
- ServletJSP学习笔记--导航
- session outline for different culture
- 双通道和单通道区别_实测内存通道的区别:单通道比双通道内存更有优势?
- 2017百度之星程序设计大赛 - 复赛 01,03,05
- JQ 取CHECKBOX选中项值
- linux c代码出现段错误,Linux下段错误(C语言)
- 最优化学习笔记(四)共轭梯度法
- Windows 7 SID 修改
- vue-cli3+typescript+路由懒加载报错问题
- 企业信息管理- 近期功能改善
- mysql 覆盖索引 简书_mysql覆盖索引与回表
- 我为什么要表扬深信服(转)
- 采集全国疫情数据(Python)
- 人证核验终端设备技术
- 关于虚拟机闪退及无法启动的问题
- 12、python 海龟绘图 turtle