zoukankan      html  css  js  c++  java
  • ASP.NET会话(Session)保存模式

    ASP.NET会话(Session)保存模式 

    今日抽空就说一下 Session .Net v1.0/v1.1 中的存储模式。大家可在 MSDN 2003 中搜索一下<sessionState>即可看到关于 Web.config 中的<sessionState>节点元素的描述,共有 OffInProcStateServerSQLServer 四种模式。OffInProc 分别指“不启用”、“进程内保存(默认值)”,此两种模式没啥讲的,所谓 InProc 就是把 Session 保存在 aspnet_wp.exe (Windows 2000 解析 ASP.NET页面所用的进程) w3wp.exe (Win2003 的进程) 中,一旦进程被中止或被重置,Session 将丢失。

    一、        引发 Session 丢失的几种原因

    动过手写代码的人都知道,Session 丢失是比较常见的事。以下是本人这几年所遇到的,能够引发 Session 丢失的原因,不敢说是百分百,丢失概率还是特别高的。错…,简直可以说是“相…当…”高哇 ^_^"

    1、    存放 Session 的电脑重启(废话,若这样都不丢,你神仙啊)

    2、    InProc 模式:aspnet_wp.exe w3wp.exe 在“任务管理器”中或其它情况下导致其进程被终止运行。

    3、    InProc 模式:修改 .cs 文件后,编译了两次(只编译一次,有时不会丢失)

    4、    InProc 模式:修改了 Web.config

    5、    InProc 模式,Windows 2003 环境:应用程序池回收、停止后重启

    6、    InProc 模式:服务器上 bin 目录里的 .dll 文件被更新

    以上列举的都是 InProc 模式下,容易引发解析 ASP.NET 应用程序重置的原因。是不是觉得很窝火?之前我也有这种感觉,慢慢就习惯啦,再后来就干脆不用这种模式了。于是乎,就有了使用下列两种模式的尝试,现写出来与大家一起分享。

    二、        使用 StateServer 保存 Session

    StateServer 模式的实质是,把Session 存放在一个单独的进程里,此进程独立于 aspnet_wp.exe w3wp.exe 。启用此服务后,在“任务管理器”中可以看到一个名为 aspnet_state.exe 的进程,下面开始说明一下设置的具体步骤:

    1、    修改注册表(关键步骤,如下图)

    运行 regedit →打开注册表→找到HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/aspnet_state/Parameters 节点→将AllowRemoteConnection 的键值设置成“1”(1 为允许,0 代表禁止)→设置Port (端口号)

    注意事项:

           a)、若ASP.NET State Service 正在运行,修改注册表内容后,则需要重新启动该服务

    b)、注意端口号的键值是以十六进制储存的,可以使用十进制进行修改,42424 是默认的端口

    c)AllowRemoteConnection 的键值设置成“1”后,意味着允许远程电脑的连接,也就是说只要知道你的服务端口,就可享用你的ASP.NET State Service,即把 Session 存放在你的电脑进程内,因此请大家慎用;键值为“0”时,仅有stateConnectionString为“tcpip=localhost: 42424”与“tcpip=127.0.0.1:42424”的情况,方可使用ASP.NET State Service

     

    2、    开启 ASP.NET State Service(如下图)

    右键点击“我的电脑”→管理→服务与应用程序→服务→双击“ASP.NET State Service”→启动(可设为“自动”)

    说明:只要安装了 .Net Framework v1.0/v1.1 ,都拥有此服务。

     

    3、    更改 Web.config

    打开 Web.config →找到<sessionState>节点内容

    <sessionState

                mode="InProc"

                stateConnectionString="tcpip=127.0.0.1:42424"

                sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes"

                cookieless="false"

                timeout="20" />

     

    →将其改为以下内容

    <sessionState mode="StateServer" stateConnectionString="tcpip=192.168.0.2:42424" timeout="20" />

    注意事项:

           a)、设成StateServer 后,必须要有对应的stateConnectionString

           b)、注意 IP 地址(可以是远程计算机 IP、计算机名称、域名)与端口号,端口号需与ASP.NET State Service 的服务端口一致

    三、        Session 放入 SQLServer 保存

    SQLServer 模式就是,把Session 存放在 SQL Server 数据库里(注意不是 Oracle ,动动脚趾都能猜到原因啦),下面开始说明一下设置的具体步骤:

    1、    启动相关的数据库服务(如图)

    运行SQL Server 服务管理器→启动 SQL Server (最好设为开机自动运行)→启动 SQL Server Agent 服务(最好设为开机自动运行)

    注意事项:

           a)、注意启动顺序,也可通过下列方式设置:右键点击“我的电脑”→管理→服务与应用程序→服务→找到“MSSQLSERVER”与“SQLSERVERAGENT”→启动并设置启动类型为“自动”

    b)SQL Server Agent在此处的作用是清除数据库中已过期的 Session

     

     

    2、    建立存放 Session DataBase

    运行“SQL 查询分析器”→使用“sa”或是拥有“master”的 db_owner 权限的用户登录数据库→打开查询文件 C:/WINNT/Microsoft.NET/Framework/v1.1.4322/InstallSqlState.sql (存放在 Windows 系统目录的 .Net 安装目录下可找到)→直接运行该 sql 脚本→刷新数据库即可看到名为 ASPState DataBase

     

     

    3、    建立连接数据库 ASPState 的用户,并为此用户授权(此步骤可跳过)

    进行此步的原因是:一是不想在 Web.config 中出现 sa 的密码;二是 tempdb 在数据库启动后仅保留 sa 一个帐号的使用权限,其余帐号的权限统统被清除,但保存 Session又需要用到此 DataBase

    A)、运行 SQL Server 的企业管理器→展开数据库的安全性→右击“登录”→新建“登录”→输入“名称”→选择“SQL Server 身份验证”→输入“密码”→指定“数据库”→点击“数据库访问”→勾选“ASPState”→选中“db_owner”角色→点击“确定”→再一次输入“密码”→点击“确定”后即可建立 ASPState 的用户(此处建立名为“SessionStateUser”,密码为“123456”的测试用户)

     

       

     

    B)、运行 SQL Server 的企业管理器→展开“管理”→展开“SQL Server 代理”→右击“作业”→点击“新建作业”→输入“名称”(此例为 GrantSessionUser )→点击标签“步骤”→新建→输入“步骤名”(此例为 Grant01)→选择数据库“tempdb”→编写 SQL 脚本“exec sp_adduser 'SessionStateUser', 'SessionUser' ,'db_owner' ”→确定→点击标签“调度”→新建→输入“名称”(此例为 Start01 )→选择类型“SQL Server 代理启动时自动启动”→确定→最后点击“确定”新增完毕

     

    C)、也可运行以下脚本一次性搞定以上 AB 两个步骤

    /******脚本开始******/

          --新建数据库帐号 SessionStateUser ,默认登录 ASPState

    EXEC sp_addlogin 'SessionStateUser', '123456', 'ASPState'

    use ASPState        --切换 DataBase

          -- SessionStateUser 授予 db_owner 的权限

    exec sp_adduser 'SessionStateUser', 'SessionUser' ,'db_owner'

    use master            --切换 DataBase

    BEGIN TRANSACTION  

          /******声明变量******/        

        DECLARE @JobID BINARY(16) 

        DECLARE @ReturnCode INT   

        SELECT @ReturnCode = 0    

        -- 若没有,则添加作业的分类

        IF (SELECT COUNT(*) FROM msdb.dbo.syscategories WHERE name = N'[Uncategorized (Local)]') < 1

            EXECUTE msdb.dbo.sp_add_category @name = N'[Uncategorized (Local)]'

        -- 新建作业

        EXECUTE @ReturnCode = msdb.dbo.sp_add_job  --调用存储过程 sp_add_job

                @job_id = @JobID OUTPUT,           --将返回的 JobID,赋值给变量

                @job_name = N'GrantSessionUser',   --作业名称

                @owner_login_name = NULL,           --默认为当前用户所有

                @description = null,

                @category_name = N'[Uncategorized (Local)]',        --作业分类归属

                @enabled = 1,                    --是否启用

                @notify_level_email = 0,

                @notify_level_page = 0,

                @notify_level_netsend = 0,

                @notify_level_eventlog = 0,

                @delete_level= 0

        IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback --出错则回滚

       

       -- 新建步骤

        EXECUTE @ReturnCode = msdb.dbo.sp_add_jobstep --调用存储过程 sp_add_jobstep

                @job_id = @JobID,                   --传入刚刚新建的 JobID

                @step_id = 1,

                @step_name = N'Grant01',         --步骤名称

                @command = N'exec sp_adduser ''SessionStateUser'', ''SessionUser'' ,''db_owner''',

                 --需要执行的 SQL 脚本(注意用两个连续的单引号表示 SQL 中的单引号)

                @database_name = N'tempdb', --执行上述 SQL 所用的 DataBase

                @server = N'',

                @database_user_name = N'',

                @subsystem = N'TSQL',    --执行类型为“Transact-SQL 脚本”

                @cmdexec_success_code = 0,

                @flags = 0,

                @retry_attempts = 0,

                @retry_interval = 1,

                @output_file_name = N'',

                @on_success_step_id = 0,

               @on_success_action = 1,

                @on_fail_step_id = 0,

                @on_fail_action = 2

        IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback

       

        -- 新建调度

        EXECUTE @ReturnCode = msdb.dbo.sp_add_jobschedule

                @job_id = @JobID,

                @name = N'Start01',   --调度名称

                @enabled = 1,

                @freq_type = 64               --64”表示当 SQLServerAgent 服务启动时运行

        IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback

       

        -- 将新建的作业添加到本地数据库

        EXECUTE @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @JobID, @server_name = N'(local)'

        IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback

       

        COMMIT TRANSACTION         

        GOTO   EndSave             

    QuitWithRollback:

        IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION

    EndSave:

    /******脚本结束******/

    4、    设置 Web.config 内容

    打开 Web.config →找到<sessionState>节点内容→修改为以下内容即可:

    <sessionState mode="SQLServer" sqlConnectionString ="data source=192.168.0.2; user id=SessionStateUser; password=123456" timeout="20" />

    注意事项:

           a)sqlConnectionString中不能出现 initial catalog 选项

    b)SQL Server Agent在此处的作用是清除数据库中已过期的 Session

    c)、你若跳过了第三步,则user id 需要用sa进行登录

    d)、若sqlConnectionString为“data source=127.0.0.1;Trusted_Connection=yes”,则使用本地计算机ASPNETWindows 2000 系统帐户)或 Network ServiceWindows 2003 系统帐户)的身份登录数据库。要是数据库不允许上述用户登录,则报错;同样,即使上述帐户能成功登录,也要分配其 tempdb 的权限,理由是 Session 是保存在 tempdb 中的,若没有该 DataBase 的存取权限是行不滴。见下图:

     关于SESSION共享的问题个人总结。

    1.不管上面采用哪种模式要达到SESION共享必须是同一个站点或者主域名相同,要不绝对不可能实现SESSION。

    2.同时相关子模块项目是不允许在IIS设置虚拟目录,设置了虚拟目录也是不可能达到共享条件的。

    3.在本地开发时怎么达到SESSION共享的条件时布置呢?举例如下:

    3.1有个大项目A站点(假设他的域名为EXAMPLE.COM.CN)

    3.2其中有2个单独的项目为B,C(假设他的域名为B.EXAMPLE.COM.CN和C.EXAMPLE.COM.CN或是EXAMPLE.COM.CN/B和EXAMPLE.COM.CN/C),他们之间要共享SESSION。

    采取做法如下(详情可以参考自己参与的DP项目):

    3.3在同一个解决方案中在A中把增加对 B,C两个Web工程的引用,注意:是工程引用,不是其他引用(且这2个工程都在A功根目录地下,但B,C工程也在这解决方案中,打开VS A工程站点是看不到B,C文件夹的,同时出现与A同等级的B,C工程站点在解决方案中,这样开发就互不影响,从而达到分工程团队开发)。

    3.4.运行时在主站点直接运行看效果。那样在本地就可以看到SESSION的共享了。

    3.5.发布到IIS上要布置3个站点,好像同时要设置COOKIE的主域名DOMIAN为主站点的域名且有以下操作

    protected void Session_Start(object sender, EventArgs e)
            {
                Response.Cookies[
    "ASP.NET_SessionId"].Value = Session.SessionID.ToString();
                Response.Cookies[
    "ASP.NET_SessionId"].Domain =".local.com";
            }

    或写个httpModules层处理这个问题

    namespace Primepress.FontSearch.Services {

        using System;

        using System.Collections.Generic;

        using System.Web;

        using System.Web.SessionState;

        using System.Reflection;

        /// <summary>

        /// 二级域名会话共享

        /// </summary>

        public class SessionSharedHttpModule : IHttpModule     {

            string _rootDomain = null; //一级域名

            public void Dispose()         {

                //throw new NotImplementedException();

            }

            public void Init(HttpApplication context)         {

                _rootDomain = "localhost"; //一级域名赋值

                //去除一级域名以外信息(将www.dhlx.cn改为dhlx.cn,如果一级域名不是常量赋值的话)

                //_rootDomain = _rootDomain.Substring(_rootDomain.LastIndexOf('.', _rootDomain.LastIndexOf('.') - 1) + 1);

                //要实现会话共享还得修改OutOfProcSessionStateStore类下的一个私有的静态字段s_uribase

                //OutOfProcSessionStateStore的声明为:

                //internal sealed class OutOfProcSessionStateStore : SessionStateStoreProviderBase

                //s_uribase的声明为:

                //static string       s_uribase;

                //关于OutOfProcSessionStateStore类以及s_uribase字段的内容请查阅OutOfProcStateClientManager.cs文件

                //文件路径:Framework源代码\V2.0.5727\dd\ndp\fx\src\xsp\System\Web\State\OutOfProcStateClientManager.cs

                Type stateServerSessionProvider = typeof(HttpSessionState).Assembly.GetType("System.Web.SessionState.OutOfProcSessionStateStore");

                FieldInfo uriField = stateServerSessionProvider.GetField("s_uribase", BindingFlags.Static | BindingFlags.NonPublic);

                if (uriField == null)

                    throw new ArgumentException("UriField was not found");

                uriField.SetValue(null, _rootDomain);

                context.EndRequest += new EventHandler(context_EndRequest);

            }

            /// <summary>

            /// 从发送给客户端的Cookie集合中找出记录会话ID的Cookie

            /// 并修改它的Domain属性值为要共享的一级域名

            /// </summary>

            void context_EndRequest(object sender, System.EventArgs e)         {

                HttpApplication app = sender as HttpApplication;

                for (int i = app.Context.Response.Cookies.Count - 1; i >= 0; i--)             {

                    //ASP.NET_SessionId是默认的存储会话ID的key,如果修改了默认值这里要修改成一致的

                    if (app.Context.Response.Cookies[i].Name.Equals("ASP.NET_SessionId"))                 {

                        app.Context.Response.Cookies[i].Domain = _rootDomain;

                        return;

                    }

                }

            }

        }

    }

     

  • 相关阅读:
    首页列表显示全部问答,完成问答详情页布局。
    制作首页的显示列表。
    管理信息系统 课程设计
    期末作品检查
    数据迁移
    模型分离(选做)
    密码保护
    实现搜索功能
    完成个人中心—导航标签
    个人中心标签页导航
  • 原文地址:https://www.cnblogs.com/scottpei/p/2913392.html
Copyright © 2011-2022 走看看