单点登录的一系列尝试及最终解决

曾经一时兴起,想搞一个实现单点登录的,然后实现起来发现了好多问题,首先原理明白,代码也很好写,如下:

Default.aspx

<%@ Page Title="" Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"%>

 

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">

</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">

<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>

<br />

<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>

<br />

<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="登陆" />

 

<asp:Button ID="Button2" runat="server" OnClick="Button2_Click" Text="注销" />

</asp:Content>

 

Default.aspx.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Caching;

using System.Web.UI;

using System.Web.UI.WebControls;

 

public partial class _Default : System.Web.UI.Page

{

protected void Page_Load(object sender, EventArgs e)

{

 

}

 

protected void Button1_Click(object sender, EventArgs e)

{

// 作为唯一标识的str_Key,应该是唯一的,这可根据需要自己设定规则。

// 做为测试,这里用用户名和密码的组合来做标识;也不进行其它的错误检查。

// 生成str_Key

string str_Key = TextBox1.Text + " " + TextBox2.Text;

// 得到Cache中的给定str_Key的值

string str_User = Convert.ToString(Cache[str_Key]);

// Cache中没有该str_Key的项目,表名用户没有登录,或者已经登录超时

if (str_User == String.Empty)

{

// TimeSpan构造函数,用来重载版本的方法,进行判断是否登录。

TimeSpan SessTimeOut = new TimeSpan(0, 0, HttpContext.Current.Session.Timeout, 0, 0);

HttpContext.Current.Cache.Insert(str_Key, str_Key, null, DateTime.MaxValue, SessTimeOut, CacheItemPriority.NotRemovable, null);

Session["User"] = str_Key;

// 首次登录成功

Response.Write("<h2 style='color:red'>你好,登录成功!");

}

else

{

// 在 Cache 中存在该用户的记录,表名已经登录过,禁止再次登录

Response.Write("<h2 style='color:red'>抱歉,您好像已经登录了!");

return;

}

}

protected void Button2_Click(object sender, EventArgs e)

{

Response.Redirect("Default2.aspx");

}

}

 

实现效果如下:

重复登录

注销之后再登录

但是往往有些用户的习惯是不会去管注销,退出一类的按钮,而是直接选择关闭浏览器,从而就会导致一类问题,我们学校的老的一款选课系统也是这样,不点注销直接关浏览器,下次登录就只能半小时后了,为了解决这个问题曾经查过很多资料,大体思路就是如何在用户关闭浏览器的时候直接清除掉session,从而是cache失去依赖项而被移除,思路上是正确的,但是随之而来的一个巨大难点是如何判断用户关闭了浏览器。从而进行了很多尝试,如下:

  1. dom里面有一个属性closed,当它为true时即浏览器窗口window关闭,但用过之后发现,这样是没办法区分刷新和关闭的,因为刷新是先close,再open,从而宣告这个方法失败。
  2. 用js判断,代码如下:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">

    <head>

    <title>Js智能判断浏览器是关闭还是刷新</title>

    <meta http-equiv="content-type" content="text/html;charset=gb2312">

    </head>

    <body>

    关闭或刷新浏览器试试!

    <script language="javascript">

    window.onbeforeunload=function(){

        var n=window.event.screenX-window.screenLeft;

        var b=n>document.documentElement.scrollWidth-20;

        if(b&&window.event.clientY<0||window.event.altKey){

            alert("关闭");

        }else{

            alert("刷新");

        }

    }

    </script>

    </body>

    </html>

    最终测试结果失败的彻彻底底,刷新事件部分浏览器能判断出来,但是关闭事件所有浏览器都无法判断

  3. 用ajax延迟判断,如一所说,dom有一个关闭属性,关闭是直接close为true,刷新则是先close,再open,我可以在页面加载的时候就open一个高和宽都为0的不可见的窗口,里面用timer将事件的判断延迟处理一下,一般来说刷新的close和open是连在一起的,所以只需要延迟几毫秒,判断有没有调用open就可以了。最后测试方法可行,但是这样会非常耗资源,给用户和服务器带来不必要的压力,不推荐使用。
  4. 心跳包算法,即页面打开后隔一段时间给服务器发一个空包,让服务器知道这个页面还活着,当用户关闭浏览器,即停止发包,心跳停止,服务器认为页面已死,清除掉session。这个得涉及到比较高端的用法,新手不建议用,很容易因为发包不当而导致ddos攻击。
  5. 结合cookie和session,cookie不设置过期时间,即为浏览器的生命周期,关闭浏览器就会清除,结合它二者判断。这是我目前找到的最有用的方法,但由于cookie是保存在用户端的,会有一些安全隐患。

昨晚趁学长在问了一下学长,才发现自己的思维一直进入了一个误区,其实没必要用户关闭浏览器就清除session,当用户关闭浏览器,然后在session过期前再次打开页面的时候,直接让用户跳过登陆,仔细想想新浪和腾讯可不就是这么干的么,最后的实现代码如下:

Default.aspx.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Caching;

using System.Web.UI;

using System.Web.UI.WebControls;

 

public partial class _Default : System.Web.UI.Page

{

protected void Page_Load(object sender, EventArgs e)

{

if (!IsPostBack)

{

string str_User = Convert.ToString(Cache[Convert.ToString(Session["User"])]);

// Cache中没有该str_Key的项目,表名用户没有登录,或者已经登录超时

if (str_User != String.Empty)

{

Response.Write("<h2 style='color:red'>你好,登录成功!");

}

}

}

 

protected void Button1_Click(object sender, EventArgs e)

{

// 作为唯一标识的str_Key,应该是唯一的,这可根据需要自己设定规则。

// 做为测试,这里用用户名和密码的组合来做标识;也不进行其它的错误检查。

// 生成str_Key

string str_Key = TextBox1.Text + " " + TextBox2.Text;

// 得到Cache中的给定str_Key的值

string str_User = Convert.ToString(Cache[str_Key]);

// Cache中没有该str_Key的项目,表名用户没有登录,或者已经登录超时

if (str_User == String.Empty)

{

// TimeSpan构造函数,用来重载版本的方法,进行判断是否登录。

TimeSpan SessTimeOut = new TimeSpan(0, 0, HttpContext.Current.Session.Timeout, 0, 0);

HttpContext.Current.Cache.Insert(str_Key, str_Key, null, DateTime.MaxValue, SessTimeOut, CacheItemPriority.NotRemovable, null);

Session["User"] = str_Key;

// 首次登录成功

Response.Write("<h2 style='color:red'>你好,登录成功!");

}

else

{

// 在 Cache 中存在该用户的记录,表名已经登录过,禁止再次登录

Response.Write("<h2 style='color:red'>抱歉,您好像已经登录了!");

return;

}

}

protected void Button2_Click(object sender, EventArgs e)

{

Response.Redirect("Default2.aspx");

}

}

 

最后实现的效果是登陆之后不注销,关闭窗口,然后再打开,就会自动登录了,截图

至此单点登录问题暂时告一段落

posted @ 2013-05-12 10:43  血之君殇  阅读(862)  评论(4编辑  收藏  举报