Edited at

[ASP.NET][IIS]別アプリケーション&別サーバでセッションを共有する方法

IIS のセッション保存は、インプロセス(デフォルト)、SQL Server、ASP.NET 状態サービス(StateServer)が可能。

インプロセスではサーバー間でセッションは共有できないため、ここでは比較的導入が簡単なStateServerでの方法を記す。

StateServerでセッションを共有する場合、アプリケーション名、machineKey が一致& サイト間で同じCookiesを利用していればよい。

アプリケーション名はWeb.configだけで設定できないので、起動時に設定するように処理を追加する必要がある。

アプリケーション名が一致しない場合、セッションIDだけを共有でき、セッションデータは共有できない。


Web.configの設定


Web.config

<configuration>

<system.web>
<!-- Cookieを共有するサイトのドメインを指定-->
<httpCookies domain=".your.site"/>
<!-- セッション StateServerにセッション保存、セッションクッキー名の指定、タイムアウト指定-->
<sessionState cookieless="UseCookies" cookieName="sessionXXXX" mode="StateServer" regenerateExpiredSessionId="false" stateConnectionString="tcpip=10.1.1.1:42424" timeout="30">
<providers>
<clear />
</providers>
</sessionState>
<!-- machineKeyを設定-->
<machineKey validationKey="B0577D3D82E341C0464C090C68681EC661DDB684E3B23166C7E8DE526490A5847152EE9EAA2928B89409FB4EB97F13BA12D11E6FABB4B8E3923364B496BA50FC" decryptionKey="93F24EC212137CC181B543FB8329C71810C351F76B0A2090CB9887811AC9DCC6" validation="SHA1" decryption="AES" />
</system.web>
<appSettings>
<!-- アプリケーション名を設定-->
<add key="ApplicationName" value="appName" />
</appSettings>

</configuration>



  • Cookieを共有するサイトのドメインを指定。ドメインを広げすぎるとセキュリティ的に良くないので注意。

    ちゃんとやるなら、IIS ARRなどでリバースプロキシを用意して実施する。(リバースプロキシだと、1つのサーバにアクセスすればよいので、ドメイン指定しなくても同じCookieが使われる。)

  • セッション保存はsessionState 句にてStateServerを使うように指定。

    ASP.NET 状態サービス(StateServer)のデフォルトポートは42424.

    セッションクッキー名は、デフォルトのままでも良いが、ドメイン内で重複して他アプリへの影響も考慮し、設定。

  • MachineKeyは簡単に作れる Web サービスが公開されているので利用

    ASP.Net MachineKey Generator

    http://www.allkeysgenerator.com/Random/ASP-Net-MachineKey-Generator.aspx

  • アプリケーション名を指定。

    後ほど処理記述にて、このアプリケーション名をセットする。


ASP.NET 状態サービス(StateServer)の設定&起動


  • State Server のファイアウォールで TCP の 42424 ポートを許可

  • State Server のレジストリを修正 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\aspnet_state\ParametersAllowRemoteConnection1

  • 「ASP.NET 状態サービス」を無効=>自動に、起動。

    「ASP.NET 状態サービス」がレジストリ修正時に起動していたら再起動。


ConfigureStateServerRemote.cmd

Rem State Server のファイアウォールで TCP の 42424 ポートを許可

netsh advfirewall firewall add rule name="ASP .NET Session State Server" dir=in action=allow protocol=TCP localport=42424

Rem 現在の値を確認
Reg Query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\aspnet_state\Parameters /v AllowRemoteConnection
Rem State Server のレジストリを修正
Rem `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\aspnet_state\Parameters` の `AllowRemoteConnection` を `1` に変更
Reg Add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\aspnet_state\Parameters /v AllowRemoteConnection /t REG_DWORD /d 1 /f

Rem 現在の値を確認
Reg Query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\aspnet_state\Parameters /v AllowRemoteConnection

Rem 「ASP.NET 状態サービス」を無効=>自動に
sc.exe config "aspnet_state" start=auto
sc stop "aspnet_state"
sc start "aspnet_state"



Global.asaxにてアプリケーション名を指定


Global.asax

using System;

using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;

public class Global : System.Web.HttpApplication
{

protected void Application_Start(object sender, EventArgs e)
{
ConfigureSession();
}

private void ConfigureSession()
{
// Get the app name from config file...
string appName = System.Configuration.ConfigurationManager.AppSettings["ApplicationName"];
if (!string.IsNullOrEmpty(appName))
{
foreach (string moduleName in this.Modules)
{
IHttpModule module = this.Modules[moduleName];

SessionStateModule ssm = module as SessionStateModule;
if (ssm != null)
{
FieldInfo storeInfo = typeof(SessionStateModule).GetField("_store", BindingFlags.Instance | BindingFlags.NonPublic);
SessionStateStoreProviderBase store = (SessionStateStoreProviderBase)storeInfo.GetValue(ssm);
if (store == null) //In IIS7 Integrated mode, module.Init() is called later
{
FieldInfo runtimeInfo = typeof(HttpRuntime).GetField("_theRuntime", BindingFlags.Static | BindingFlags.NonPublic);
HttpRuntime theRuntime = (HttpRuntime)runtimeInfo.GetValue(null);
FieldInfo appNameInfo = typeof(HttpRuntime).GetField("_appDomainAppId", BindingFlags.Instance | BindingFlags.NonPublic);
appNameInfo.SetValue(theRuntime, appName);
}
else
{
Type storeType = store.GetType();
if (storeType.Name == "OutOfProcSessionStateStore")
{
FieldInfo uribaseInfo = storeType.GetField("s_uribase", BindingFlags.Static | BindingFlags.NonPublic);
uribaseInfo.SetValue(storeType, appName);
}
}
}
}
}
}
}



参照元