Http And Session - webcofig

A session as you probably mean it is a server-side object which stores state. You use it in servlets to store and retrieve data. You keep hearing people saying HTTP is a stateless protocol, right? They mean when you load a page, you're finished as far as the web server is concerned. If you reload a page, the new request isn't associated in any way with the previous one.

A cookie is a small piece of information a browser sends to a server with every request.

Most servlet containers use a cookie to identify a session.

1) The user's browser requests a servlet.
2) The servlet container creates a session.
3) The servlet gives the session a unique ID.
4) The servlet sets a cookie in the browser with this ID.
5) Let's say the servlet then store's the user's name in
   the session.
5) The user requests another servlet on the same server.
   As part of the request, the cookie with the session ID
   is sent back to the server.
6) Since the servlet container is told which session to use,
   it make it available again.
7) So servlet #2 can retrieve the user's name, since we
   put it in the session, and say, "Hi, Bob."


一.      设置web.config相关选项

先启用窗体身份验证和默认登陆页,如下。

<authentication mode="Forms">

       <forms loginUrl="default.aspx"></forms>

</authentication>

设置网站可以匿名访问,如下

<authorization>

           <allow users="*" />

</authorization>

然后设置跟目录下的admin目录拒绝匿名登陆,如下。注意这个小节在System.Web小节下面。

     <location path="admin">

       <system.web>

           <authorization>

              <deny users="?"></deny>

           </authorization>

       </system.web>

</location>

http请求和发送的编码设置成GB2312,否则在取查询字符串的时候会有问题,如下。

<globalization requestEncoding="gb2312" responseEncoding="gb2312" />

设置session超时时间为1分钟,并启用cookieless,如下。

<sessionState mode="InProc" cookieless="true" timeout="1" />

为了启用页面跟踪,我们先启用每一页的trace,以便我们方便的调试,如下。

<trace enabled="true" requestLimit="1000" pageOutput="true" traceMode="SortByTime" localOnly="true" />

二.      设置Global.asax文件

处理Application_Start方法,实例化一个哈西表,然后保存在Cache

    protected void Application_Start(Object sender, EventArgs e)

    {

       Hashtable h=new Hashtable();

       Context.Cache.Insert("online",h);

}

    Session_End方法里调用LogoutCache()方法,方法源码如下

/// <summary>

    /// 清除Cache里当前的用户,主要在Global.asaxSession_End方法和用户注销的方法里调用      /// </summary>

    public void LogoutCache()

    {

       Hashtable h=(Hashtable)Context.Cache["online"];

       if(h!=null)

       {

           if(h[Session.SessionID]!=null)

           h.Remove(Session.SessionID);

           Context.Cache["online"]=h;

       }

}

三.      设置相关的登陆和注销代码

登陆前调用PreventRepeatLogin()方法,这个方法可以防止用户重复登陆,如果上次用户登陆超时大于1分钟,也就是关闭了所有admin目录下的页面达到60秒以上,就认为上次登陆的用户超时,你就可以登陆了,如果不超过60秒,就会生成一个自定义异常。在Cache["online"]里保存了一个哈西表,哈西表的key是当前登陆用户的SessionID,Value是一个ArrayList,这个ArrayList有两个元素,第一个是用户登陆的名字第二个元素是用户登陆的时间,然后在每个admin目录下的页刷新页面的时候会更新当前登陆用户的登陆时间,而只admin目录下有一个页打开着,即使不手工向服务器发送请求,也会自动发送一个请求更新登陆时间,下面我在页面基类里写了一个函数来做到这一点,其实这会增加服务器的负担,但在一定情况下也是一个可行的办法.

/// <summary>

       /// 防止用户重复登陆,在用户将要身份验证前使用

       /// </summary>

       /// <param name="name">要验证的用户名字</param>

       private void PreventRepeatLogin(string name)

       {

           Hashtable h=(Hashtable)Cache["online"];

           if(h!=null)

           {

              IDictionaryEnumerator e1=h.GetEnumerator();

              bool flag=false;

              while(e1.MoveNext())

              {                

                  if((string)((ArrayList)e1.Value)[0]==name)

                  {

                     flag=true;

                     break;

                  }

              }

              if(flag)

              {

                  TimeSpan ts=System.DateTime.Now.Subtract(Convert.ToDateTime(((ArrayList)e1.Value)[1]));

                  if(ts.TotalSeconds<60)

                     throw new oa.cls.MyException("对不起,你输入的账户正在被使用中,如果你是这个账户的真正主人,请在下次登陆时及时的更改你的密码,因为你的密码极有可能被盗窃了!");

                  else

                     h.Remove(e1.Key);       

              }

           }

           else

           {

              h=new Hashtable();

           }

           ArrayList al=new ArrayList();

           al.Add(name);

           al.Add(System.DateTime.Now);

           h[Session.SessionID]=al;

           if(Cache["online"]==null)

           {

              Context.Cache.Insert("online",h);

           }else

              Cache["Online"]=h;         

    }

用户注销的时候调用上面提到LogoutCache()方法

四.      设置admin目录下的的所有页面的基类

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.HtmlControls;

using System.Collections;

 

 

 

 

namespace oa.cls

{

    public class MyBasePage : System.Web.UI.Page

    {

 

       /// <summary>

       /// 获取本页是否在受保护目录,我这里整个程序在OA的虚拟目录下,受保护的目录是admin目录

       /// </summary>

       protected bool IsAdminDir

       {

           get

           {

              return Request.FilePath.IndexOf("/oa/admin")==0;

           }

       }

 

       /// <summary>

       /// 防止session超时,如果超时就注销身份验证并提示和转向到网站默认页

       /// </summary>

       private void PreventSessionTimeout()

       {

           if(!this.IsAdminDir) return;

           if(Session["User_Name"]==null&&this.IsAdminDir)

           {            

              System.Web.Security.FormsAuthentication.SignOut();

              this.Alert("登陆超时",Request.ApplicationPath)

           }

       }

       /// <summary>

       /// 每次刷新本页面的时候更新Cache里的登陆时间选项,在下面的OnInit方法里调用.

       /// </summary>

       private void UpdateCacheTime()

       {

           Hashtable h=(Hashtable)Cache["online"];

           if(h!=null)

           {

              ((ArrayList)h[Session.SessionID])[1]=DateTime.Now;

           }

           Cache["Online"]=h;

       }

       /// <summary>

       /// 在跟踪里输出一个HashTable的所有元素,在下面的OnInit方法里调用.以便方便的观察缓存数据

       /// </summary>

       /// <param name="myList"></param>

       private void TraceValues( Hashtable myList) 

       {

           IDictionaryEnumerator myEnumerator = myList.GetEnumerator();

           int i=0;

           while ( myEnumerator.MoveNext() )

           {

              Context.Trace.Write( "onlineSessionID"+i, myEnumerator.Key.ToString());

              ArrayList al=(ArrayList)myEnumerator.Value;

              Context.Trace.Write( "onlineName"+i, al[0].ToString());

              Context.Trace.Write( "onlineTime"+i,al[1].ToString());

              TimeSpan ts=System.DateTime.Now.Subtract(Convert.ToDateTime(al[1].ToString()));

               Context.Trace.Write("当前的时间和此登陆时间间隔的秒数",ts.TotalSeconds.ToString());

              i++;

           }

       }

 

       /// <summary>

       /// 弹出信息并返回到指定页

       /// </summary>

       /// <param name="msg">弹出的消息</param>

       /// <param name="url">指定转向的页面</param>

       protected void Alert(string msg,string url)

       {

           string scriptString = "<script language=JavaScript>alert(/""+msg+"/");location.href=/""+url+"/"</script>";

           if(!this.IsStartupScriptRegistered("alert"))

              this.RegisterStartupScript("alert", scriptString);

       }

       /// <summary>

       /// 为了防止常时间不刷新页面造成会话超时,这里写一段脚本,每隔一分钟向本页发送一个请求以维持会话不被超时,这里用的是xmlhttp的无刷新请求

       /// 这个方法也在下面的OnInit方法里调用

       /// </summary>

       protected void XmlReLoad()

       {     

           System.Text.StringBuilder htmlstr=new System.Text.StringBuilder();

           htmlstr.Append("<SCRIPT LANGUAGE=/"JavaScript/">");

           htmlstr.Append("function GetMessage(){");

           htmlstr.Append("  var xh=new ActiveXObject(/"Microsoft.XMLHTTP/");");

           htmlstr.Append("  xh.open(/"get/",window.location,false);");

           htmlstr.Append("  xh.send();");

           htmlstr.Append("  window.setTimeout(/"GetMessage()/",60000);");

           htmlstr.Append("}");

           htmlstr.Append("window.οnlοad=GetMessage();");

           htmlstr.Append("</SCRIPT>       ");

           if(!this.IsStartupScriptRegistered("xmlreload"))

              this.RegisterStartupScript("alert", htmlstr.ToString());

       }

 

       override protected void OnInit(EventArgs e)

       {

           base.OnInit(e);

           this.PreventSessionTimeout();

           this.UpdateCacheTime();

           this.XmlReLoad();

           if(this.Cache["online"]!=null)

           {

              this.TraceValues((System.Collections.Hashtable)Cache["online"]);

 

           }

       }

    }

 

}

五.      写一个自定义异常类

首先要在跟目录下写一个错误显示页面ShowErr.aspx,这个页面根据传递过来的查询字符串msg的值,在一个Label上显示错误信息。

using System;

 

namespace oa.cls

{

    /// <summary>

    /// MyException 的摘要说明。

    /// </summary>

    public class MyException:ApplicationException

    {

 

           /// <summary>

           /// 构造函数

           /// </summary>

           public MyException():base()

           {

           }

           /// <summary>

           /// 构造函数

           /// </summary>

           /// <param name="ErrMessage">异常消息</param>

           public MyException(string Message):base(Message)

           {

               System.Web.HttpContext.Current.Response.Redirect("~/ShowErr.aspx?msg="+Message);

           }

           /// <summary>

           /// 构造函数

           /// </summary>

           /// <param name="Message">异常消息</param>

           /// <param name="InnerException">引起该异常的异常类</param>

           public MyException(string Message,Exception InnerException):base(Message,InnerException)

           {

           }

       }

}

六.总结

我发现在Session里保存的值,比如session["name"]是没有任何向服务器的请求达到1分钟后就会自动丢失,但是session ID是关闭同一进程的浏览器页面后达1分钟后才会丢失并更换的,因为只要你开着浏览器就会有session ID,无论是在url里保存还是在cookies里。不知道这个结论对不对,反正我在设置了sessiontimeout1分钟后,session["name"]的值已经没有了,可是SessionID还是旧的,Global.asax里的Session_End里的代码也没有执行,而身份验证票据也没有丢失。我不知道这三者之间的关系是怎样的,谁先谁后,好像在<authentication>小节可以设置一个timeout属性






using System.Collections.Generic;
public class OnlineManage
{
private static OnlineManage _instance = new OnlineManage();
//private Dictionary<int, OnlineUserInfo> _onlineUserList = new Dictionary<int, OnlineUserInfo>();

private Dictionary<string, OnlineUserInfo> _onlineSession = new Dictionary<string, OnlineUserInfo>();

public const int Timeout_Minutes = 20; //非登录会员在线用户过期时间

public const int Timeout_Minutes_Member = 40; //登录会员在线过期时间


private OnlineManage()
{
}

public void Clear()
{
lock (this)
{
//_onlineUserList.Clear();

_onlineSession.Clear();
}
}

/// <summary>

/// 获取当前在线用户总数(包含登录和未登录的)

/// </summary>

public int Count
{
get { return _onlineSession.Count; }
}

/// <summary>

/// 在线会员数

/// </summary>

public int MemberCount
{
get
{
int iRet = 0;
lock (this)
{
foreach (string _sKey in _onlineSession.Keys)
{
OnlineUserInfo oInfo = _onlineSession[_sKey];
if (oInfo == null)
{
_onlineSession.Remove(_sKey);
}
else
{
if (oInfo.UserID > 0)
{
iRet++;
}
}
}
}
return iRet;
}
}

public OnlineUserInfo this[int UserID]
{
get
{
lock (this)
{
foreach (string _sKey in _onlineSession.Keys)
{
OnlineUserInfo oInfo = _onlineSession[_sKey];
if (oInfo == null)
{
_onlineSession.Remove(_sKey);
}
else
{
if (oInfo.UserID == UserID)
{
return oInfo;
}
}
}
return null;
}
}
}

public OnlineUserInfo this[string sSessionID]
{
get
{
if (_onlineSession.ContainsKey(sSessionID))
return _onlineSession[sSessionID];
else
return null;
}
}

/// <summary>

/// 获取所有当前在线用户的ID数组

/// </summary>

public int[] OnlineUserIDs
{
get
{
lock (this)
{
List<int> ids = new List<int>();
foreach (string _sKey in _onlineSession.Keys)
{
OnlineUserInfo oInfo = this[_sKey];
if (oInfo != null && oInfo.UserID > 0)
{
ids.Add(oInfo.UserID);
}
}
return ids.ToArray();
}
}
}

/// <summary>

/// 用户有活动,存在异常情况:

/// 1. Session未结束,但是换了用户登录

/// </summary>

/// <param name="UserID"></param>

public void UserActived(string sSessionID, int iUserID)
{
lock (this)
{
OnlineUserInfo oInfo = this[sSessionID];
if (oInfo == null)
{
oInfo = new OnlineUserInfo(sSessionID, iUserID);
_onlineSession.Add(sSessionID, oInfo);
}
else
{
if (oInfo.UserID != iUserID) //考虑同一个页面先后用别的帐号登录

{
oInfo.UserID = iUserID;
oInfo.LoginTime = DateTime.Now;
}
oInfo.LastActiveTime = DateTime.Now;
}
}
}

/// <summary>

/// 用户离开(未活动时间超时或者用户注销了)

/// </summary>

/// <param name="UserID"></param>

public void UserLeaved(string sSessionID, int iUserID)
{
lock (this)
{
_onlineSession.Remove(sSessionID);
}
}

/// <summary>

/// 用户离开(未活动时间超时或者用户注销了)

/// </summary>

/// <param name="sSessionID"></param>

public void UserLeaved(string sSessionID)
{
lock (this)
{
_onlineSession.Remove(sSessionID);
}
}


/// <summary>

/// 检查在线用户状态,如果发现有超时未活动的用户,将其删除

/// </summary>

public void CheckOnlineUsers()
{
lock (this)
{
//1.先查询出需要删除的Key列表

List<string> _removeKeys = new List<string>();
foreach (KeyValuePair<string, OnlineUserInfo> kvp in _onlineSession)
{
OnlineUserInfo oInfo = kvp.Value;
string _sKey = kvp.Key;
if (oInfo == null)
{
_removeKeys.Add(_sKey);
}
else
{
if (oInfo.UserID > 0) //登录会员

{
if (oInfo.NoActiveMinutes > Timeout_Minutes_Member)
{
_removeKeys.Add(_sKey);
}
}
else //未登录会员
{
if (oInfo.NoActiveMinutes > Timeout_Minutes)
{
_removeKeys.Add(_sKey);
}
}
}
}

//2.从容器中删除这些Key

foreach (string _key in _removeKeys)
{
_onlineSession.Remove(_key);
}
_removeKeys.Clear();
}
}

/// <summary>

/// 获取唯一实例

/// </summary>

/// <returns></returns>

public static OnlineManage GetInstance()
{
return _instance;
}
}

/// <summary>

/// 在线用户信息类

/// </summary>

public class OnlineUserInfo
{
public int UserID;
public string SessionID;
public DateTime LoginTime;
public DateTime LastActiveTime;

/// <summary>

/// 构造函数,用于用户新登录时创建用户在线条目

/// </summary>

/// <param name="iUserID">活动的用户ID</param>

/// <param name="dtLoginTime">用户活动时间</param>

public OnlineUserInfo(int iUserID, DateTime dtLoginTime)
{
UserID = iUserID;
SessionID = "";
LoginTime = dtLoginTime;
LastActiveTime = dtLoginTime;
}

/// <summary>

/// 构造函数,用于用户新登录时创建用户在线条目

/// </summary>

/// <param name="iUserID">活动的用户ID</param>

/// <param name="dtLoginTime">用户活动时间</param>

public OnlineUserInfo(string sSessionID, int iUserID)
{
UserID = iUserID;
LoginTime = DateTime.Now;
LastActiveTime = DateTime.Now;
SessionID = sSessionID;
}

/// <summary>

/// 用户初次进来并创建了新Session

/// </summary>

/// <param name="sSessionID"></param>

public OnlineUserInfo(string sSessionID)
{
UserID = 0;
LoginTime = DateTime.Now;
LastActiveTime = DateTime.Now;
SessionID = sSessionID;
}

/// <summary>

/// 获取该用户的未活动时长(分钟数)

/// </summary>

public int NoActiveMinutes
{
get
{
TimeSpan dtSpan = DateTime.Now.Subtract(LastActiveTime);
return (int) dtSpan.TotalMinutes;
}
}

/// <summary>

/// 获取该用户的在线时长(分钟数)

/// </summary>

public int OnlineMinutes
{
get
{
TimeSpan dtSpan = DateTime.Now.Subtract(LoginTime);
return (int) dtSpan.TotalMinutes;
}
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值