设为首页
加入收藏
联系我们
首页 基础教程 技术文档 实例分析 数 据 库 疑难杂症 ASP.NET 七夕许愿树 技术论坛
最新文章
  .NET上传图片加文字…
 在vs.net bate…
 C#中利用正则表达式实现…
 Visual C#的SQ…
 关于使用存储过程创建分页
 通用分页显示查询存储过程
 大数据量的分页
 什么是web.confi…
 ASP.NET 配置文件…
 对“三层结构”的深入理解…
 多个关键字的查询问题
 VB和C# 语法对比图 …
 C#基础全接触
 用C#压缩和修复Acce…
 C#中的类型相等与恒等(…
推荐文章
 关于C#中的REF和黓认…
 读书笔记c#高级编程 委…
 【算法】C#快速排序类
 Visual C#的SQ…
 C#中调用API
 Infragistics…
 C#接口转换
 C#读取设备信息
 用.net操作word
 C# MessageBo…
 Visual C#中的数…
 雅虎公司C#笔试题,看看…
 C#.NET使用NHib…
 .net学习之运算符重载…
 Visual C# 3.…
热门文章
 ADO.Net与ADO在…
 开发ASP.NET下的M…
 用C#+XMI技术进行U…
 什么是虚拟机?
 C#基础全接触
 C#中利用正则表达式实现…
 雅虎公司C#笔试题,看看…
 用Visual C# 2…
 C#学习第一天
 C#语言初级入门(1)
 VB和C# 语法对比图 …
 远程重启计算机(C#)
 什么是B/S三层?
 Visual C#的SQ…
 Visual C#常用函…
.net窗体身份验证方案
阅读正文 文字大小:增大 减小  文字行距:增大 减小   双击自动滚屏
本文由中国C#技术学习中心整理  如果你对本文有不明之处请到技术论坛讨论!

一套.net窗体身份验证方案( 解决了防止用户重复登陆, session超时等问题 ) 一.设置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.asax的Session_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的虚拟目录下, 受?
    さ哪柯际莂dmin目录
       /// </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.onload = 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里.不知道这个结论对不对, 反正我在设置了session的timeout为1分钟后, session["name"]的值已经没有了, 可是SessionID还是旧的, Global.asax里的Session_End里的代码也没有执行, 而身份验证票据也没有丢失.我不知道这三者之间的关系是怎样的, 谁先谁后, 好像在<authentication>小节可以设置一个timeout属性, 不过项目赶的紧, 我没时间研究了.
     以上这些代码比较零散, 我花费了2天的时间才总结出来这套方案, 不是很完美, 但是暂时只能这样了, 不能在这方面浪费很多时间了, 大家可以把上面的代码组织到一个类里, 然后把方法都修改成静态方法方便调用.
     最后大家有什么建议和改进的意见欢迎和我交流.

本文由中国C#技术学习中心整理  如果你对本文有不明之处请到技术论坛讨论!

中国C#技术交流QQ群:6337034  10976424  9383681  35248582  35248645
版权所有:中国C#技术学习中心 Copyright © 20010-20012
建议浏览分辨率使用:1024*768分辨率
粤ICP备05002251号