ハンドラーとルーティング
すべてのハンドラー、そのアクション、そして Router.bx が URL をそれらに結びつける方法。
このページ内
ハンドラーとルーティング
ハンドラーマップ
| ハンドラー | ベース | 目的 |
|---|---|---|
AuditLog.bx | BaseSecureHandler | 監査トレイルの閲覧、エクスポート、パージ |
Assets.bx | EventHandler | ユーザーアバターとブランディングロゴをストリーミング配信 |
Auth.bx | EventHandler | ログイン、登録、招待、パスワードリセット - すべて公開 |
BaseSecureHandler.bx | RestHandler | すべての管理ハンドラーの基底クラス |
Dashboard.bx | BaseSecureHandler | 認証済みユーザーのランディングページ |
Main.bx | EventHandler | 暗黙イベントハンドラー - アーキテクチャ を参照 |
Permissions.bx | BaseSecureHandler | 権限スラッグの CRUD |
Profile.bx | BaseSecureHandler | セルフサービスのプロフィール、パスワード、API トークン、パスキー |
Roles.bx | BaseSecureHandler | ロールの CRUD とユーザー割り当て |
Settings.bx | BaseSecureHandler | アプリ設定レジストリ |
Users.bx | BaseSecureHandler | ユーザー管理 |
BaseSecureHandler
すべての保護されたハンドラーは BaseSecureHandler を継承しており、その preHandler はすべての状態変更リクエストで CSRF を検証し、Admin レイアウトを強制し、cbRequirePasskey がオンでユーザーがパスキーを持っていない場合は profile/passkey-required にリダイレクトします。また、共有ヘルパー(getApiResults()、ensureSortDirection()、getPagination())も提供します。
component extends="coldbox.system.RestHandler" {
function preHandler( event, rc, prc ){
// ...CSRF verification, deny-by-default...
event.setLayout( "Admin" );
// ...passkey enforcement...
}
}
新しい保護されたハンドラーを構築するときは、いつも同じところから始まります。
component extends="BaseSecureHandler" secured {
function index( event, rc, prc ){
prc.pageTitle = "My Page";
event.setView( "myhandler/index" );
}
}
AuditLog
クラスレベルで @secured("auditlog:admin,auditlog:read")。index 以外のすべてのアクションは @remote です。
index、search、show- 監査トレイルの閲覧とフィルタリングexport-@secured("auditlog:admin,auditlog:export")、CSV をストリーミング配信purge-@secured("auditlog:admin,auditlog:delete")、カットオフより古いエントリを削除clear-@secured("auditlog:admin")、すべてのエントリを削除
Assets
クラスレベルでの @secured アノテーションはありません - 非公開の cbfs assets ディスク(データベースと ORM と app/config/modules/cbfs.bx を参照)からバイナリファイルをストリーミング配信します。このディスクは Web ルートの外にあり、それ以外の方法では到達できません。
avatar-@secured(認証済みの任意のユーザー)、ユーザーのsm/lgアバター JPEG バリアントをストリーミング配信logo- 公開、sm/lgのブランディングロゴ PNG バリアントをストリーミング配信し、ログイン画面などのゲストページがそれをレンダリングできるようにします
どちらのアクションも、userId/size の形式が認識できない場合や、要求されたファイルが単に存在しない場合には(エラーではなく)404 を返すため、呼び出し元はレスポンスの形からは「アバターなし」と「そのようなユーザーなし」を区別できません。リサイズ、クロップ、ストレージはすべて ImageService(app/models/system/ImageService.bx)を通じて行われ、@inject プロパティではなく各アクション内で getInstance() を使って呼び出されます - その理由については Assets.bx の docblock を参照してください(ハンドラーがトリガーするシングルトン構築に関する WireBox の起動順序の癖です)。
Auth
@secured アノテーションはありません - これらのアクションはゲストにも到達可能である必要があります。
login/doLogin(GET/POST) - CSRF 検証済み、securityService.login()を呼び出し、rememberMeをサポートregister/doRegister-cbAllowRegistration設定によってゲートcheckEmailAvailability- ライブなメールアドレス利用可否チェックのための JSON エンドポイントverifyRegistration-PURPOSE_REGISTRATIONアクショントークンを消費activateInvitation/doActivateInvitation- 招待された、管理者が作成したユーザーにパスワードを設定forgotPassword/doForgotPassword-cbAllowForgotPasswordによってゲートresetPassword/doResetPassword- リセットトークンを検証し、新しいパスワードを設定verifyEmailChange-PURPOSE_EMAIL_CHANGEアクショントークンを消費logout-securityService.logout()を呼び出し
preHandler は、すでに認証済みの訪問者をダッシュボードへ直接リダイレクトし、レイアウトを prc.settings.cbLoginLayout(デフォルトは AuthSplit - guides/security.md を参照)から設定します。verifyEmailChange と logout はそのリダイレクトから除外されているため、訪問者が認証済みかどうかにかかわらず到達可能です。
Dashboard
@secured(認証済みの任意のユーザー、特定の権限は不要)。
index- ダッシュボードのホームnotAuthorized-invalidAuthorizationEventのターゲットで、認証済みユーザーに必要な権限がない場合に表示されます
Permissions
クラスレベルで @secured("permissions:admin,permissions:read")。
indexcreate-@secured("permissions:admin,permissions:write")update/delete-@remote、同じ write/delete 権限
Profile
現在のユーザーのためのセルフサービスアクションで、@secured が付いており、index を除いてすべて @remote の AJAX エンドポイントです。
index、passkeyRequiredsave、doPasswordChangerequestEmailChange/cancelEmailChange- 保留中のメールアドレス変更を開始/キャンセルし、Auth.verifyEmailChangeを通じて確認されますlistTokens/createToken/updateToken/deleteToken- API トークンlistPasskeys/updatePasskey/deletePasskeyuploadAvatar/deleteAvatar- 画像をrc.avatar内の base64 データ URI として受け付けます(BoxLang には multipart/form-data パーサーがないため、アップロードは JSON として送られます)。BaseSecureHandler.decodeDataUri()でデコードされ、Assets.avatarによってストリーミングで返されます
これらのすべては、安全な HTTP メソッドで到達されない限り、BaseSecureHandler によって CSRF 検証されます - CSRF 検証 を参照してください。
Roles
クラスレベルで @secured("roles:admin,roles:read")。index 以外のすべてのアクションは @remote です。
indexcreate/update/delete-@secured("roles:admin,roles:write"/"...:delete")users/availableUsers- ロールに割り当て済み/割り当て可能なユーザーを一覧表示addUser/removeUser-@secured("roles:admin")
Settings
クラスレベルで @secured("settings:admin,settings:read")。
indexregistry/registrySearch- ページネーション付きの設定レジストリcreateRegistry/updateRegistry/toggleRegistryStatus/deleteRegistry-settings:admin,settings:writesave- コア設定の一括保存uploadLogo/deleteLogo-settings:admin,settings:write、Profile.uploadAvatarと同じ base64 データ URI の規約。cbAppLogo設定を保存/復元し、Assets.logo経由でストリーミングして返します- 管理ユーティリティ(すべて
settings:admin):clearTemplateCache、clearSessionsCache、revokeRememberTokens、flushSettingsCache
Users
クラスレベルで @secured("users:admin,users:read")。
index、searchcreate/update/delete/resendInvitation-users:admin,users:write/...:deleteshow-users:read- 管理者専用(
users:admin):updateProfile、setStatus、resetPassword、verify、revokeRememberTokens、addRole/removeRole、addPermission/removePermission、savePreferences、revokeToken/revokeAllTokens
ensureNotSelf() は、これらのうちいくつかを保護し、管理者が自分自身のロールを降格・削除することを防ぎます。

CSRF 検証
app/config/modules/cbsecurity.bx は csrf.enableAutoVerifier: false を設定しているため、グローバルなインターセプターは存在しません。代わりに、BaseSecureHandler.preHandler() が、これを継承するすべてのハンドラーに対して デフォルト拒否 の方式で CSRF を検証します。
static {
// The safe methods of RFC 9110, exempt from CSRF verification below.
SAFE_HTTP_METHODS = "GET,HEAD,OPTIONS"
}
function preHandler( event, rc, prc ) {
if (
!static.SAFE_HTTP_METHODS.listFindNoCase( event.getHTTPMethod() )
&& !csrfVerify( rc.csrf ?: "" )
) {
return onInvalidCSRF( argumentCollection = arguments )
}
// ...
}
保護されたハンドラーを継承するとき、これが意味することは次のとおりです。
- オプトインするものはありません。
POST、PUT、PATCH、DELETEで到達するどのアクションも、追加した日から有効なrc.csrfを持たなければなりません。更新を忘れないよう覚えておくべきハンドラーごとのリストはありません。 - 安全なメソッドは免除されます。
GET、HEAD、OPTIONSは状態を変更してはならないため CSRF のリスクを持たず、OPTIONS(CORS のプリフライト)はそもそもトークンを持てません。もしコード内の安全なメソッドが状態を変更しているなら、それが修正すべきバグです。 onInvalidCSRF()はオーバーライド可能です。 基本実装は認可失敗として中断します。これは JSON/AJAX エンドポイントが求めるものです -Permissionsのすべてのミューテーションは、リダイレクトではなく古いトークンから復旧するfetchWithCsrf()(フロントエンド を参照)を通じて送信される、まさにこの種類のものになりました。Settingsは依然としてこれをオーバーライドし、ネイティブなフォーム送信に対してメッセージをフラッシュしてリダイレクトするため、ブラウザのフォームは裸の 403 ではなくページを受け取ります。JSON ではなく HTML をレンダリングする自分のハンドラーでは、これをオーバーライドしてください。
Auth は BaseSecureHandler ではなく coldbox.system.EventHandler を継承しています。そのアクションは未認証の訪問者のために実行されるため、上記のチェックを継承できないからです。各状態変更アクションは、それぞれ独自にトークンを検証します。doLogin、doRegister、doActivateInvitation、doForgotPassword、doResetPassword、logout です。
ルートマップ (app/config/Router.bx)
すべてのルートは 1 つの configure() 関数内で宣言されます。
route( "/healthcheck" ).to( () => "Ok!" );
get( "dashboard" ).to( "Dashboard.index" );
resources( "permissions", parameterName = "permissionId" );
route( "roles/:roleId/available-users" ).to( "Roles.availableUsers" );
route( "roles/:roleId/users" ).toAction( { POST: "addUser" } );
route( "roles/:roleId/users/:userId" ).toAction( { DELETE: "removeUser" } );
resources( "roles", parameterName = "roleId" );
resources( "users", parameterName = "userId" );
route( "profile" ).toAction( { GET: "index", POST: "save" } );
// @app_routes@ ← insertion point for module/scaffold-generated routes
route( ":handler/:action?" ).end(); // conventions-based catch-all
すべてのメソッド、URL、ターゲットアクション、必要な権限の完全な表については、リファレンス: ルートマップ を参照してください。