这个话题网上有了很多种做法,数据库里设字段、session记录等。但没有能完美解决问题的,问题关键在于网页关闭时如何使其登录状态为null,强制关闭浏览器,截任务管理器线程、意外客户端断电等,无法捕捉。
可以使用页面刷新向服务器发请求?服务器负荷会不会高?
   另外我使用的是iframe框架,怎么进行定时刷新,谁有完整的代码(包括登录时设置无法重复登录,页面定时刷新,浏览器关闭时置登录状态为null)

解决方案 »

  1.   

    补充一下我用的是struts2+spring+hibernate。
      

  2.   

     同步Session控制参考http://topic.csdn.net/u/20110125/13/e51455bd-bfcd-4cb5-85e5-bfd6eff22475.html?155429651
      

  3.   

    问题关键在于网页关闭时如何使其登录状态为null?
    js里面有一个onunload事件,页面关闭的时候触发,在onunload事件中可以用ajax跟后台交互。
      

  4.   

    我之前做得在关闭浏览器时销毁登录账号是这样做的,强制关闭浏览器,截任务管理器线程这个是可以实现的,但是登录状态改为null没有,我们数据库没有设置状态的这个字段,可以意外客户端断电也没尝试过
      

  5.   

    你可以用spring security去进行控制
      

  6.   

    我的个人看法,不要限制新登录的,而是让新登录的,把老的顶下去就行了。实现方法你自己考虑吧。基本上是每个用户一个标识,在登录时生成,每次运行都要判断cookie里面的标识是否相同,如果不同则有新的登录,自动logout.
      

  7.   

    详情看这里
    http://blog.csdn.net/java2000_net/archive/2011/06/22/6560509.aspx
      

  8.   

    这个问题如果用纯B/S架构去做,目前是没有解决方案的,或者说是无法实现的。因为网页关闭有很多种方法,比如突然断电、死机、自然灾害等。
    唯一的方法是,写浏览器插件,浏览器客户端下载插件并安装后,该插件在客户端电脑开端口,定时向服务器端口发送数据证明其存在。服务器端判定,如果超过某些时间,未收到指定客户端的信息,则自动认为该客户端已关闭。
    这种单点登陆的问题,很多地方都有实现,而且并不是楼上所说,各大网站都未实现。我在做产品时就遇到这个问题,交给C++开发人员写个ActiveX或ocx控件,完美实现。
      

  9.   

    顶10楼版主大人不要限制新登录的,而是让新登录的,把老的顶下去就行了。我们现在就是这样做的,这样可称完美方案。实现的方法很多,我们的做法是维护了一个session vs 用户id对照表,顺便还可以观察在线用户情况
      

  10.   

    单点登录的概念不是这样的,用SessionListener吧。
      

  11.   

    sessionListener可以解决,每次登陆检查用户名对应的session在服务器端是否已经销毁,如果没有销毁,提示重复登陆即可
      

  12.   

    可以参考下:
    http://blog.csdn.net/zxingchao2009/archive/2011/03/24/6274378.aspx
      

  13.   

    那像qq那样后一个登陆时把前一个已登陆用户挤掉,大家有没有代码呢?另外onunload事件并非能解决刷新呀,最小化后关闭浏览器的事件吧。弄这个很郁闷 现在发现网上能找到的东西,对于比较难得问题, 一半都是抄 一半都是思路,哎。真正能行的通的好少啊,希望大家讨论下,能贴代码的最好贴一点,互相学习下
      

  14.   

    spring security 这个好做么?有资料没 工期有点紧 所以只好求助大家 快点弄好了。我也是头次碰到这样的问题,b/s还有这样的需求。无语 这个能很好的解决的话,那在线统计,聊天室什么的都是一个道理了。我下了一堆这样的例子,不过都是做了一部分,考虑的不周全。
      

  15.   

    可以这样做:
    1 登录时,生成一个唯一的标识,然后和用户名关联。 标识保存到session或者cookie里面,用于下次访问。
    2 下次访问时,判断标识与用户名关联的是否一样,如果不一样则判断重复登录了,本session自动注销。
      

  16.   

    sessionListener可以解决,每次登陆检查用户名对应的session在服务器端是否已经销毁,如果没有销毁,提示重复登陆即可
      

  17.   

    难道没人会spring security么?我看了下官方文档,貌似可以设置。
    <http>
        ...
        <session-management>
            <concurrency-control max-sessions="1" />
        </session-management>
      </http>
    这是文档上说的,但我把其中的spring-security-core.jar以及spring-security-web.jar加入项目后,拿它的application-security.xml配置修改,但没成功。
    18楼上说的“不要限制新登录的,而是让新登录的,把老的顶下去就行了。”还有你的思路能贴下代码么?
      

  18.   

    我现在用的系统是这样的,比如老的点击保存之后,跳到登录页面,输入密码之后继续保存动作,类似于session过期
      

  19.   

    哎 我试半天spring security了,没成功,请提到这个的高手说点什么好吗?别做说那2个英文单词呀,思路讨论了这么久,大致已经说完了,请做过的 贴点实用的代码,别从网上弄过来,能copy的我都照着做了,不行,才来这里请教各位的了
      

  20.   

    唉!深深同情呀。楼主祝你好运。Good luck!
      

  21.   

    可以看看国内php开源的网站 他们都提供了单点登录的方法  里面有些思想还是很好的啊  像discuz的ucenter和phpcms的phpsso 都是还是很不错的啊 
      

  22.   

    我以前在javaWeb实现过,大致原理就是 用户登录后  用户id 和 sessionId  绑定保存在服务器中 然后写一个过滤器 过滤所有 *.jsp,*.do, action请求,如果通过当前用户id获取的sessionId 和当前sessionId 不一致则退出登录跳到登录界面实现的效果就是 用户1 使用用户 xxx登录后  用户2 再用 xxx登录,如果用户1当前再做任何操作则被踢出;当前这个解决方法只在但服务器下有效,集群效果下就没用啦。姑且当时给楼主1个思路看可取不。
      

  23.   

    过滤器代码可以参考参考
    public class SignonFilter implements Filter {
        private FilterConfig filterConfig;    private String login_page = UserList.LOGIN_PAGE;    public void destroy() {
            this.filterConfig = null;    }    public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse res = (HttpServletResponse) response;
            //获取当前用户
            SysUsersAssociators userVo = LoginHelper.getLoginVo((HttpServletRequest)request);
       
            String servletPath=  req.getServletPath();
            String isGuest = new String();
             if((String)req.getSession().getAttribute("guest")==null)
             {
              isGuest = "yes";  //前台用户登录 不需要单点控制         }
             else
             {
              isGuest = "no";   //需要单点控制         }
            //如果isGuest 为yes表示其为普通用户        if ((userVo!=null&&!userVo.getUsersAssId().equals("")) //登录页和登录action不判断                || servletPath.equals( "/" +"userAss.do")||servletPath.equals( "/" +"landing.jsp")||servletPath.equals( "/" +"loginout.jsp")
                    ||isGuest.equals("yes"))
                    {
            
                chain.doFilter(req, res);
            } else {
                String url = req.getContextPath()  + "/" + login_page;
                //返回主页面    
                res.setContentType("text/html;charset=GBK");
                PrintWriter out = res.getWriter();
                out.write("<script language=\"javascript\">\r\n");
                out.println("alert('登录超时或该用户在别处登录,请您重新登录!');window.location.href =\"" + url + "\";");
                out.write("</script>");
                //返回当前页时使用。            //   res.sendRedirect(url);
            }
        }    public void init(FilterConfig config) throws ServletException {
            this.filterConfig = config;
        }
    }
      

  24.   


    后来挤掉先前的:与用户关联一个sessonid的字段, 就是每次与服务器交互判断与服务器记录的session是否相同,不同就重新登录新的session替换原来的限制登录:与用户关联一个sessionid字段和时间字段,每次与服务器交互判断与服务器记录的sessionid是否相同,相同就把当前时间更新的时间字段,不同并且当前时间与系统存的时间在一定时间内(比如5分钟)就返回不让登录,如果系统sessionid和时间字段是空,就让登录。    另外在关闭网页事件上尽量控制清空sessionid和时间字段。  -------------此方法就是突然断电或者关闭进程5分钟内无法登录。
      

  25.   

    ServletContextListener 用监听器 contextDestroyed不行吗?
      

  26.   

    1. 采用虚拟用户的方式
    比喻用户admin
    A君登陆了  那么就是自动生成admin1
    B君登陆了  那么就生成admin2这样 可以限制一个账号同时登陆的个数 可以自己定2. 采用自制web框架,直接在浏览器中弹出新框架,给框架分配ID标识,最先得到标识的人可以登陆但是这个是你自己定
    简单吧
      

  27.   

    请教下,我是用的iframe框架,每个action处理后得到的页面都在我的工作区iframe上,这样弹出的登录页面就会嵌套在我的iframe里,怎么办?
      

  28.   

    请把login.action中 将登录人员信息写入session,以及web中怎么配置这个过滤器代码贴下,没用过过滤器。
      

  29.   

    像qq那样后一个登陆时把前一个已登陆用户挤掉?原理其实很简单。也根本不需要理会浏览器关掉的问题
    1、A同志登录的时候 生成一串Guid标识 对应 用户名 放到 字典中(字典需要做成单例或者放到Cache中,这样每个人访问的才会是同一个对象,且Guid和用户名是一一对应的) ,这个时候 字典 中就存在了A用户的 一个登录票据Token,这个票据同时要保存一份到Session中。
    2、B同志使用了和A同志同一个账号登录的时候,又生成了一串Guid标识 ,先到字典中根据用户名查找出对应的Guid标识,是否和B同志新生成的Guid标识对应,这里肯定是不会对应了,那么将旧的Guid删除,替换上新的Guid3、脚本派上用场了,在浏览器端做个Ajax轮询,具体要设置成几秒要看服务器压力了,这个轮询的主要目的就是要去检查  当前登录票据和在字典中的票据是否一致。这里模拟一下踢出的过程,这时A的浏览器会定时发出验证操作,去验证存储在Session中的票据的Guid 是否和字典中的票据是否一致,如果不一致了,被B给更新了,不一致的话,就把Session注销,完事!如果一致,就什么事都不做
      

  30.   

    所以整个代码的顺序是
    先检查,如果存在就更新,不存在就添加,浏览器做ajax轮询验证
      

  31.   

    web.xml配置<!-- 设置单点监听 -->
    <filter>
    <filter-name>sso</filter-name>
    <filter-class>com.xxxx.util.SSO.SignonFilter</filter-class>
    </filter>
      <filter-mapping>
    <filter-name>sso</filter-name>
    <url-pattern>*.do</url-pattern>
    </filter-mapping> 
     <filter-mapping>
    <filter-name>sso</filter-name>
    <url-pattern>*.jsp</url-pattern>
    </filter-mapping> 
    <!-- 设置单点监听 -->
    这是登录要做的事情,用户id绑定seesionId放入到applicatio中
    application的生命周期是从服务器启动到服务器关闭ServletContext  application = request.getSession().getServletContext(); //获取服务器对象
    application.setAttribute(sua.getUsersAssId(),request.getSession().getId());  //用户id和其sessionid绑定
                
    application.setAttribute("associators_info", sua); //用户登录放入服务器
    session.setAttribute("guest","no"); //表示不是前台用户 受单点控制这个解决方案目前的缺点就是集群服务器下无效,楼主切记
      

  32.   

    补充对应楼主的需要 你登录只做2行代码就OK了ServletContext  application = request.getSession().getServletContext(); //获取服务器对象
    application.setAttribute(用户id,账号,request.getSession().getId());  //用户id和其sessionid绑定
      

  33.   

    请教下,我是用的iframe框架,每个action处理后得到的页面都在我的工作区iframe上,这样弹出的登录页面就会嵌套在我的iframe里,怎么办?
    对于楼主的这个问题我不大清楚你需要什么样的效果,但是不管什么样的页面效果跳转也好,弹出也罢无非都是些js去操作,楼主请注意看这
    String url = req.getContextPath()  + "/" + login_page;
                //返回主页面    
                res.setContentType("text/html;charset=GBK");
                PrintWriter out = res.getWriter();
                out.write("<script language=\"javascript\">\r\n");
                out.println("alert('登录超时或该用户在别处登录,请您重新登录!');window.location.href =\"" + url + "\";");
                out.write("</script>");
    你当前要实现的页面效果以JS输出就OK了
      

  34.   

     这个方法不错,准备这样来实现。有新的登录时,查询这个对照表,用户id相同但sessionId不同的,就将旧的session踢掉,将新的sessionId加入对照表。
    “单点登录”的概念说的是,多个相关系统,登录其中一个后,再访问其他系统就不需要登录了。几个系统可以共享session。而不是这里讨论的这个问题吧。
      

  35.   


    可以加过滤器,在过滤器中判断未登录或是登录失效的,就跳转到一个中间页面。该页面就一句js脚本。
    top.location="/**/login.htm";
    这样中间页面先在iframe中存在,执行完脚本,主页面就跳到登录页面了。需要注意的是,如果过滤器拦截的是ajax发出的请求,该方法无效,需要单独处理。
      

  36.   

    曾经T宝3面的时候,面试官问到单点登录的,, 他的意思是数据库里面做字段标示在线状态.至于你说的怎么通知数据库下线的问题, 是通过过滤器(或类似的玩意儿)每次操作的时候更新一下一个web容器标示 来标示我还在线,然后定时的自检,在多少分钟之内没有进行操作web容器标示就会消失(可以用其他类似原理的东西标示) 就将数据库里面的值进行更改。。
    回头我想了想,有时候你在用一些应用的时候,掉线了暂时上不去或者提示你用户正在线,也说明其实是实现思路跟这个都差不多。
    希望对你有帮助··
      

  37.   

    好像有点麻烦。
    1.使用session listener通知登录和登出,并进行帐号和sessionid之间的管理,建一个usermanagerment(问题:负载均衡时怎么办?放数据库中?)
    2.页面关闭方法onunload中使用ajax通知服务器(问题:其他关闭情况怎么办?使用一个固定的页定时用ajax向服务器发送活动消息,并设置定时时间和判断超时时间)
    3.限制同一个人重复登录,像csdn这样二周不用登录,使用cookie(如spring security所说,不要放用户和密码,最后时加密后留存到数据库中,把加密后的东西写到session中.)
    4.是否踢出前一个,默认不踢出,可配置为踢出。不踢出时要有机制可以通知业务操作管理员投诉或取消锁定(另加功能呀)
    不知可以否?
      

  38.   

    10楼说的,spring Security可以实现的
      

  39.   

    思路:
    1 在数据库中保持每个用户的登录状态。这个我觉得可以保证一个用户在任何时候只能有一个人登录,但是有一个弊端,不知道怎样保证这个用户在另外一台机器上登录,然后把原来登录的机器自动下线。
    2 判断session。这个只能保证同一用户在同一台机器上登录一次。
      

  40.   

    做一个session监听器,然后自己做一个全局的map存放所用的用户信息,当session产生或销毁时,对这个map进行处理就可以解决你这个问题了。但是有个小问题你得注意,在WebSphere下session处理有点问题,tomcat没问题,这个你可以自己找找原因。
      

  41.   

    自己写一个 定时器 放到 listener 里 监听
      

  42.   

    如果使用memcache好象就可以了吧
    登录的时候,检查memcache中有无此用户登录状态,如果已经登录,就提示已经登录。
    如果没有,就登录进去,然后在memcache里记下状态,再设置超时时间,一般一分钟就可以了。
    唯一的缺点:上一个登录用户意外退出,有可能一分钟以后才可以再登录进去。
      

  43.   

    虽然memchache是c写的,但是对web程序一样很好用,不能忽视之
      

  44.   


    我在研究security的时候也是考虑到这个问题,security里面也提供里两种方式,一种是前者进去后者不能进,一种是前者进,后者把前者给挤出去,第二种方法才是王道,第一种方法security也不能完美解决,意外情况是不能捕捉到的,简单的说下security对于session的控制,他会在登录时候把session存到一个库里面,然后供验证匹配,大概思路就是这样,具体可以来我的博客参考我对security的研究
      

  45.   

     string token = Guid.NewGuid().ToString();//令牌
                            //注册身份令牌,uid参数是用户的账号
                            WebApplication1.Classes.TokenManagement.Register(uid, token);               
                            HttpCookie tokenCookie = new HttpCookie("passport");
                            tokenCookie.Values.Add("uid", uid);
                            tokenCookie.Values.Add("token", token);
    http://www.jjski.net 
      

  46.   

    楼主看看我写的,能够满足你的需求。
    地址:http://blog.csdn.net/chenghui0317/article/details/9373345