Skip to content

Session に保存される情報と共有範囲 ​

第1版作成 最終更新 (日本時間)
確認バージョン1.5.8.1

プリザンターの Session は、ログイン状態だけでなく、画面の表示条件、編集中の設定、画面遷移をまたぐメッセージ、認証処理の途中の情報を保持します。同じ保存機構は、アプリ全体の排他ロックやデータ保護キー、SMTP の OAuth トークンにも使われます。

値を読むときは、誰の値かを示す SessionGuid、用途を示す Key、ページを示す Page の組で見ると整理できます。このページでは、保存内容に加えて保存先、読み書きの流れ、保持期間も説明します。

調査範囲 ​

記述は本体ソースの確認によるものです。実環境の Session の採取や、各機能を実行した際の保存内容の測定は行っていません。参照先はコミット 8d29c7bd110c2487ad4b39bd34dafaf0ba66373b に固定しています。

最初に区別する4つの情報 ​

名前内容存続する範囲
Context.SessionGuidCookie から復元する独自のセッション識別子同じセッション Cookie を使うリクエスト
Context.SessionDataその識別子と現在のページに対応する保存値を読み込んだ辞書辞書自体は現在の Context。元の値は保存先に残る
Context.UserSessionData@{ユーザー ID} を識別子として読み込んだ保存値の辞書元の値はユーザー単位でリクエストをまたぐ
サーバースクリプトの context.UserDataContext が生成する ExpandoObject をスクリプトへ渡すもの同じ Context 内。Session への永続化処理はない

根拠は Context.cs#L46-L57、Context.cs#L331-L394、Context.cs#L482-L518、Context.cs#L629-L640、ServerScriptModel.cs#L97-L106 です。サーバースクリプトの context.UserData を、Sessions API の保存値と同じものとして扱わないでください。

ASP.NET Core の HttpContext.Session も設定されていますが、本体の Context.SetSessionData() を呼ぶ箇所は確認したソース内にありません。以下で扱う値は SessionUtilities を使う独自の保存機構の値です(Startup.cs#L128-L135、Context.cs#L1295-L1307)。

保存先と設定 ​

App_Data/Parameters/Session.json の既定値は次のとおりです(Session.json)。

json
{
    "RetentionPeriod": 1440,
    "UseKeyValueStore": false
}

RetentionPeriod の単位は分で、既定値は 24 時間です。ただし RDB では期限を過ぎた瞬間のアクセスを拒否する設定ではなく、古い行を削除する際の基準です。Redis では書き込み時の TTL に使います。

設定・値の種類書き込み先格納方法
UseKeyValueStore: falseRDB の Sessions テーブルSessionGuid・Key・Page の組を主キーとする行
UseKeyValueStore: true かつ内部値RedisGUID をキーとするハッシュ。ページ単位のフィールド名は {Key}_{Page}
UseKeyValueStore: true かつ UserArea の値RDB の Sessions テーブルRedis 利用時も API 用の任意値は RDB に分岐

Value は文字列で、複雑なオブジェクトは各機能が JSON などへ変換して保存します。RDB には一回読み込み用の ReadOnce と API 領域を示す UserArea もあります(SessionUtilities.cs#L141-L192、Sessions_Key.json、Sessions_Page.json)。

読み込みのたびに RDB と Redis を統合する実装ではありません。Get() は UseKeyValueStore で読み込み先を決めるため、Redis 有効時の API 用任意値は書き込み先との不一致が生じます(SessionUtilities.cs#L31-L86)。

保存範囲はブラウザ、ユーザー、アプリ共有に分かれる ​

図を読み込み中…

SessionGuid代表的な値読み書きする主体
Cookie Pleasanter_SessionGuid の GUIDView、SiteSettings、Message などその Cookie を送るリクエスト
@{ユーザー ID}ユーザー単位の View、User_{任意キー}同じユーザーとして解決されたリクエスト
認証チケットごとに新規生成する GUIDAuthenticationTicketCookie 認証のチケットストア
SessionExclusiveTableExclusive_SiteId={サイト ID}サイト処理の排他制御
@AspNetCoreDataProtectionKeys名前をキーとする XML既定のデータ保護キーリポジトリ
SmtpOAuthTokenOAuthToken:{クライアント ID}SMTP の OAuth 認証処理

根拠は Views.cs#L9-L224、SessionUtilities.cs#L323-L390、AuthenticationTicketStore.cs#L14-L77、SessionExclusive.cs#L11-L103、AspNetCoreKeyManagementXmlRepository.cs#L14-L65、Smtp.cs#L222-L295 です。

ここでいうブラウザ単位は Cookie による区分です。タブを識別するキーはなく、同じ Cookie と同じ Page で保存された値を各タブが読みます。一方、@{ユーザー ID} の値は Cookie の GUID が異なる場合も同じ保存先を使います。

Page はタブ番号ではなく画面の識別子 ​

page: true の書き込みでは context.Page を保存します。items のページは items/{サイト ID}、ゴミ箱はその末尾に /trashbox が付きます。page: false の値は空文字のページに保存されます(Context.cs#L443-L471、SessionUtilities.cs#L141-L192)。

RDB の読み込み対象は Page が空文字か現在の context.Page と一致する行です。Context.SessionData には全サイトの値を一括で読み込むわけではありません(SessionUtilities.cs#L31-L86)。

ログインと認証処理で扱う情報 ​

キー内容保存範囲・読み手
AuthenticationTicketシリアライズした認証チケットを Base64 にした文字列チケット専用 GUID。AuthenticationTicketStore が復元
SwitchLoginId切り替え先のログイン IDCookie の GUID、ページ共通。特権ユーザーの切り替え処理
Passkeys_fido2.attestationOptionsパスキー登録要求のオプションの JSONCookie の GUID、ページ共通。登録応答の検証処理
Passkeys_fido2.assertionOptionsパスキーログイン要求のオプションの JSONCookie の GUID、ページ共通。認証応答の検証処理

認証チケットは通常の画面用 GUID と別に保存する ​

AuthenticationTicketStore.StoreAsync() は GUID を新規生成してチケットを保存し、その GUID を Cookie 認証基盤に返します。更新・取得・削除も、このチケット用 GUID で行います(AuthenticationTicketStore.cs#L14-L77、Startup.cs#L150-L193)。

フォーム認証のサインイン処理では ClaimTypes.Name にログイン名を入れた ClaimsPrincipal と認証プロパティを作ります。ユーザー情報の解決は別途 Context が行い、Cookie 認証のログイン名や、指定された API キーから Users の情報を取得します。認証チケットをユーザーマスターの全項目のコピーとして理解するのは誤りです(Context.cs#L1327-L1337、Context.cs#L482-L518)。

AuthenticationTicket の Base64 化はチケットの保存形式です。独自の GUID を暗号化して保存する Pleasanter_SessionGuid の Cookie と、役割も形式も異なります。

切り替え先とパスキーの処理途中の情報 ​

SwitchLoginId はユーザー切り替え時に書かれ、切り替えの解除で削除されます。Context のユーザー解決でも参照されます(UserUtilities.cs#L5217-L5291、Context.cs#L482-L518)。

パスキーでは、登録・認証の要求を作る際にオプションを保存し、ブラウザからの応答を受けた処理が取り出して削除します。登録済みパスキーの一覧は別途 PasskeyCollection から取得しています。ここで保存するのは、要求と応答を結び付けるための処理途中の情報です(PasskeysController.cs#L26-L166、PasskeysController.cs#L223-L308、PasskeyUtilities.cs#L250-L292)。

一覧・カレンダーなどの表示状態 ​

キー保存内容範囲
Viewフィルター、ソート、検索語、表示項目、各表示方式の条件などを選別した JSONページ単位。保存種別に応じて Cookie の GUID または @{ユーザー ID}
View_{テーブル名}リンク先テーブル用のビューの JSONページ単位。通常のビューと同様に保存種別で分岐
ViewModeサイト ID と表示アクションを対応付けた辞書の JSONCookie の GUID、ページ共通

Views.GetBySession() は保存されたビューを復元し、フォームの変更を反映して保存します。「ユーザ」の場合は UserSessionData を読み、保存先を @{ユーザー ID} に切り替えます。「保存しない」の場合は書き込みを抑止します。MCP からの処理でも保存を抑止します(Views.cs#L9-L224)。

ビューの保存種別保存先と共有範囲
セッションCookie の GUID に対応するページ単位の値。同じ Cookie を使う要求で復元
ユーザ@{ユーザー ID} に対応するページ単位の値。別の Cookie の要求でも同じユーザーなら復元
保存しないビュー変更を Session に書き込まない

View に入るものはフィルターだけではない ​

保存するのは View 全体をそのまま JSON にしたものではなく、View.GetRecordingData() で作った記録用のオブジェクトです。空の値や既定値との一致など、項目ごとに保存条件があります(View.cs#L1458-L1885)。

分類保存対象となるプロパティの例
基本の表示Id、Name、DefaultMode、GridColumns
絞り込み・検索ColumnFilterHash、ColumnFilterSearchTypes、ColumnFilterNegatives、Search、Own、Incomplete
ソートColumnSorterHash
パネルの表示FiltersDisplayType、FiltersReduced、AggregationsReduced
カレンダーCalendarDate、CalendarStart、CalendarEnd、CalendarViewType、CalendarGroupBy
ダッシュボードDashboardPartLayoutHash、パーツ別のカレンダー状態の各ハッシュ
クロス集計CrosstabGroupByX、CrosstabGroupByY、CrosstabAggregateType、CrosstabValue
ガントチャートGanttGroupBy、GanttSortBy、GanttPeriod、GanttStartDate
時系列・分析TimeSeriesGroupBy、TimeSeriesAggregateType、TimeSeriesChartType、AnalyPartSettings
カンバンKambanGroupByX、KambanGroupByY、KambanValue、KambanColumns

ViewMode はこれとは別のキーです。サイトごとの context.Action を一つの辞書に保存します。読み込み時は既定ビューの DefaultMode があればそちらを優先し、保存値がなければ index を使います(ViewModes.cs#L9-L48)。

管理画面で編集中の情報 ​

設定画面の操作では、複雑な設定を Session に置いて次の要求へ引き継ぎます。たとえばサイト設定変更時は SiteSettings.RecordingJson() を SiteSettings キーに保存し、フォームにその項目がない場合は保存値から復元します(SiteModel.cs#L2512-L2525、SiteModel.cs#L1937-L1941)。

キー内容保存範囲・定義
SiteSettingsサイト設定の記録用 JSONCookie の GUID、ページ単位。SiteModel.cs#L404-L548
MonitorChangesColumns変更監視の項目リスト同上。SiteModel.cs#L404-L548
TitleColumnsタイトル項目のリスト同上。SiteModel.cs#L404-L548
Exportエクスポート設定同上。SiteModel.cs#L404-L548
DisableSiteCreatorPermissionサイト作成者の権限に関する設定値同上。SiteModel.cs#L404-L548
UserSettingsユーザー設定の JSONCookie の GUID、ページ単位。UserModel.cs#L849-L906
MailAddressesメールアドレスのリスト同上。UserModel.cs#L849-L906
TenantSettingsテナント設定Cookie の GUID、ページ単位。TenantModel.cs#L422-L450
BinarySettingsバイナリの設定Cookie の GUID、ページ単位。BinaryModel.cs#L220-L248

これらの読み取りメソッドは、Session に値がなければモデルのプロパティを使います。Session の値は編集中の状態を引き継ぐ役割があり、レコードに確定保存された設定と区別して読む必要があります。

メッセージ、表示補助、一時ファイル ​

キー内容・用途根拠
StartTime、LastAccessTimeセッション開始時刻と要求間隔の計算に使う時刻。文字列として保存SessionUtilities.cs#L198-L214、Context.cs#L710-L720
Message作成・削除・コピーなどの後、次の画面に出すメッセージの JSON。ReadOnce を指定SessionUtilities.cs#L236-L243
Responsiveレスポンシブ表示設定の真偽値の文字列ResourcesController.cs#L39-L49、Context.cs#L629-L640
Language未ログイン時の言語決定で使う言語コード。クエリ文字列で指定された値を保存Context.cs#L861-L903
ClosedAnnouncement:{お知らせ ID}閉じたお知らせを示す文字列 trueUserUtilities.cs#L5348-L5360、HtmlHeaders.cs#L108-L120
ExceptionSiteIdエラー画面からの案内に使う、管理可能なサイト IDHandleErrorExAttribute.cs#L37-L41、HtmlTemplates.cs#L330-L358
TempFile_{GUID}一時ファイルのダウンロードを許可する印。値は空文字BinaryUtilities.cs#L700-L739
ファイル名・対象 ID・GUID から生成した SHA-512 ハッシュ添付 API の分割アップロードを継続するための一時 GUIDBinariesController.cs#L115-L122、BinariesController.cs#L340-L358

値が空でも意味がある ​

一時ファイルの許可は値の内容でなく、TempFile_{GUID} というキーの存在で判定します。ファイル本体を Session に格納する処理ではありません。分割アップロードでも、Session に置く値は一時 GUID です。

ReadOnce は RDB と Redis で処理が異なる ​

RDB の Get() は対象の値を SELECT し、ApiRequestBody == null の場合に、その GUID の ReadOnce 行を削除して取得結果を辞書で返します。削除の条件に Page はありません。画面に表示された瞬間を検出して消す処理ではありません(SessionUtilities.cs#L31-L86)。

Redis では {GUID}_readOnce を別ハッシュとして読み、ハッシュごと削除します。こちらには RDB と同じ API 本文による削除抑止がありません。

Sessions API の任意値 ​

Sessions API の値には User_ という接頭辞が付き、UserArea: true として保存されます。たとえば SessionKey が MyApp.Preference なら、内部のキーは User_MyApp.Preference です(SessionUtilities.cs#L283-L317)。

設定SessionGuid共有範囲
SavePerUser: falsecontext.SessionGuid同じ Session Cookie の要求
SavePerUser: true@{context.UserId}同じユーザーの要求

API の Get は GetUserArea() を呼び、要求されたキーに必ず User_ を付けて検索します。SessionKey に AuthenticationTicket や SiteSettings を指定しても、同名の内部キーを読む処理にはなりません。Sessions API は内部 Session 全体を閲覧する API ではありません(SessionUtilities.cs#L323-L390)。

また、RDB では Context が UserArea の値を含めて読み込むのは Controller == "sessions" のときです。通常画面の SessionData には API 用の値を含めません(Context.cs#L629-L640、Context.cs#L482-L518)。

操作は /api/sessions/set、/api/sessions/get、/api/sessions/delete への POST です。認証されたユーザーの範囲で処理し、保存時は空でない SessionKey と SessionValue、取得・削除時は空でない SessionKey を要求します。保存・取得・削除のいずれも、同じキーと SavePerUser の指定を使います。取得対象が存在しない場合は NotFound を返します(SessionUtilities.cs#L323-L390、SessionUtilities.cs#L396-L429、SessionsController.cs#L18-L67)。

上のパスはルート配置の場合です。サブディレクトリ配置ではアプリケーションの公開パスを先頭に付けます。

アプリ全体で共有する情報 ​

サイト処理の排他ロック ​

SessionExclusive という識別子の下に、サイト ID、ユーザー ID、呼び出し元などのコメント、更新時刻を持つロック情報を JSON で保存します。テーブル単位のキーは TableExclusive_SiteId={サイト ID} です。

ロック取得時は更新時刻から 120 秒を超えた値を失効したものとして扱い、更新処理には 30 秒の更新間隔の判定があります。これらはロックの鮮度の判定で、Session.json のデータ保持期間とは別です(SessionExclusive.cs#L11-L103)。

データ保護キー ​

既定のキーリポジトリでは @AspNetCoreDataProtectionKeys に名前をキーとする XML を保存します。Cookie などを保護・復号するためのアプリ共有情報です。既定構成では XML 内のキーを暗号化する AspNetCoreKeyManagementXmlEncryptor も設定されます(Startup.cs#L273-L311、AspNetCoreKeyManagementXmlRepository.cs#L14-L65、AspNetCoreKeyManagementXmlEncryptor.cs#L9-L21)。

Security.json のデータ保護設定によって Azure Blob Storage や別指定の Redis を使う経路もあります。したがって、この識別子の値が常に存在するとは限りません。

SMTP の OAuth アクセストークン ​

SmtpOAuthToken の識別子と OAuthToken:{クライアント ID} のキーで、Token と ExpiresAtUtc を持つ JSON を保存します。トークン有効期限が現在時刻と OAuthTokenRefreshBufferTime による余裕時間を超えるときだけ再利用し、それ以外は再取得します(Smtp.cs#L222-L295)。

ブラウザの表示状態とは独立したメール送信処理のキャッシュです。Session の保存値を調査結果として共有するときは、トークンや認証チケット、メールアドレス、保護キーの XML を本文やログへ転載しないでください。

データの寿命を一つの期限で理解しない ​

対象寿命を決める処理
通常の RDB の保存値UpdatedTime と RetentionPeriod による日次削除。GUID 全体ではなく行ごとの判定
@ で始まる RDB の保存値通常の日次削除の対象外。ユーザー単位の値や保護キーが該当
Redis のハッシュ書き込み時に RetentionPeriod 分の TTL を設定。@ で始まる識別子にも適用
Message上記に加えて ReadOnce の読み込み時削除
パスキーの要求オプション登録・認証の応答処理で明示的に削除
認証チケットCookie 認証基盤の更新・削除と保存先の保持処理
排他ロック更新時刻による独自の鮮度判定と処理終了時の削除
SMTP トークン保存先の保持処理に加えて ExpiresAtUtc の有効性判定

根拠は SessionUtilities.cs#L432-L452、SessionUtilities.cs#L141-L192 と各機能の参照先です。@ の削除除外は RDB の条件であり、Redis で無期限になるという意味ではありません。

ログイン時の通常 GUID のローテーションは、既存の値を新しい GUID へ移す処理です。ユーザー単位やアプリ共有の識別子を一緒に振り直す処理ではありません(Context.cs#L1445-L1521)。

調査で見る順番 ​

  1. 症状に関係するキーを上の一覧から選びます。表示条件なら View と ViewMode、設定画面なら SiteSettings などが候補です。
  2. SessionGuid が Cookie の GUID、@{ユーザー ID}、機能専用の識別子のどれかを確認します。
  3. ページ単位なら Page が一致するか、ビューなら保存種別がどれかを確認します。
  4. UseKeyValueStore と UserArea を確認し、実際の保存先と読み込み経路を調べます。
  5. ReadOnce、明示削除、保持期間、機能独自の有効性判定を分けて確認します。

Redis の設定時もユーザー領域への書き込みは RDB へ分岐しますが、Get() は Redis を読みます。また、Redis のフィールド名は _ でキーとページを分割します。キー自体に _ がある Passkeys_... や TempFile_... は、後半がページとして判定されて読み込みから除外されたり、読み込まれても前半だけのキーになったりします。RDB と同じキーが復元されると考えず、保存時のフィールド名と復元時の分割を照合してください(SessionUtilities.cs#L31-L86)。

関連ページ ​

変更履歴

第1版Sessionに保存される情報と共有範囲の解説を追加