Nest.js 認証検証方法の例

Nest.js 認証検証方法の例

0x0 はじめに

システム認可とは、ログインしたユーザーが操作を実行するプロセスを指します。たとえば、管理者はシステム上でユーザー操作を実行したり、Web サイトの投稿を管理したりできますが、管理者以外のユーザーは投稿の許可された読み取りなどの操作を実行できます。したがって、システム認可を実装するには、ID 認証メカニズムが必要です。以下は、最も基本的なロールベースのアクセス制御システムの実装です。

0x1 RBAC 実装

ロールベースのアクセス制御 (RBAC) は、ロール権限や定義済みポリシーに依存しないアクセス制御メカニズムです。まず、システム ロール列挙情報を表す role.enum.ts ファイルを作成します。

エクスポート列挙型ロール{
 ユーザー = 'user'、
 管理者 = 'admin'
}

より複雑なシステムの場合は、管理を改善するためにロール情報をデータベースに保存することをお勧めします。

次に、デコレータを作成し、@Roles() を使用して、アクセスに必要な指定されたリソース ロールを実行します。roles.decorator.ts を作成します。

'@nestjs/common' から { SetMetadata } をインポートします。
'./role.enum' から { Role } をインポートします。

エクスポート const ROLES_KEY = 'roles'
エクスポート const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles)

上記は、@Roles() という名前のデコレータを作成します。これは、ユーザーの作成など、任意のルート コントローラを装飾するために使用できます。

@役職()
@Roles(ロール.管理者)
作成(@Body() createUserDto: CreateUserDto): Promise<UserEntity> {
 this.userService.create(createUserDto) を返します。
}

最後に、現在のユーザーに割り当てられているロールと現在のルーティング コントローラーに必要なロールを比較する RolesGuard クラスを作成します。ルーティング ロール (カスタム メタデータ) にアクセスするには、Reflector ツール クラスを使用します。新しい roles.guard.ts を作成します。

'@nestjs/common' から { Injectable、CanActivate、ExecutionContext } をインポートします。
'@nestjs/core' から { Reflector } をインポートします。

'./role.enum' から { Role } をインポートします。
'./roles.decorator' から { ROLES_KEY } をインポートします。

@インジェクタブル()
RolesGuardクラスをエクスポートし、CanActivateを実装します。
 コンストラクター(プライベートリフレクター: Reflector) {}

 canActivate(コンテキスト: ExecutionContext): ブール値 {
 const requireRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY、[context.getHandler(), context.getClass()])
 役割が必要な場合
  真を返す
 }
 const { ユーザー } = context.switchToHttp().getRequest()
 requireRoles.some(role => user.roles?.includes(role)) を返します
 }
}

request.user に roles 属性が含まれていると仮定します。

クラスユーザー{
 // ...その他のプロパティ
 役割: 役割[]
}

次に、RolesGuard がコントローラーにグローバルに登録されます。

プロバイダー:
 {
 提供: APP_GUARD、
 使用クラス: RolesGuard
 }
]

ユーザーがロールの範囲を超えたリクエストにアクセスする場合:

{
 「ステータスコード」: 403,
 "メッセージ": "禁止されたリソース",
 「エラー」: 「禁止」
}

0x2 クレームベースの承認

アイデンティティを作成した後、システムは 1 つ以上の宣言的権限をアイデンティティに割り当てることができます。これは、現在のユーザーが何であるかではなく、現在のユーザーに何をすべきかを指示することを意味します。Nest システムでは、宣言的承認は上記の RBAC と同様の方法で実装されていますが、違いがあります。特定のロールを判断するのではなく、権限を比較する必要があります。各ユーザーには、@RequirePermissions() デコレータを定義してから必要な権限属性にアクセスするなど、一連の権限が割り当てられます。

@役職()
@RequirePermissions(権限.CREATE_USER)
作成(@Body() createUserDto: CreateUserDto): Promise<UserEntity> {
 this.userService.create(createUserDto) を返します。
}

権限は、システムがアクセスできる権限グループを含む PRAC のロール列挙に似ています。

エクスポート列挙型ロール{
 CREATE_USER = ['追加'、'読み取り'、'更新'、'削除']、
 READ_USER = ['読み取り']
}

0x3 統合 CASL

CASL は、クライアントがアクセスするルーティング コントローラー リソースを制限できる同種認証ライブラリです。インストールの依存関係:

糸を追加 @casl/ability

以下は、CASL メカニズムを実装し、User と Article という 2 つのエンティティ クラスを作成する最も簡単な例です。

クラスユーザー{
 id: 番号
 isAdmin: ブール値
}

User エンティティ クラスには、ユーザー ID と、ユーザーに管理者権限があるかどうかという 2 つの属性があります。

クラス記事{
 id: 番号
 isPublished: ブール値
 著者ID: 文字列
}

Article エンティティ クラスには、記事番号、記事のステータス (公開されているかどうか)、記事を書いた著者番号という 3 つの属性があります。

上記の最も単純な 2 つの例に基づいて、最も単純な関数を作成できます。

  • 管理者権限を持つユーザーは、すべてのエンティティ(作成、読み取り、更新、削除)を管理できます。
  • ユーザーはすべてのコンテンツに対して読み取り専用アクセス権を持ちます
  • ユーザーは自分の記事を更新できます authorId === userId
  • 公開された記事は削除できません。article.isPublished === true

上記の関数では、エンティティに対するユーザーの操作を表す Action 列挙体を作成できます。

エクスポート列挙アクション{
 管理 = '管理'、
 作成 = '作成'、
 読む = '読む'、
 更新 = '更新'、
 削除 = '削除'、
}

Manage は CASL の特別なキーワードであり、任意の操作を実行できることを意味します。

関数を実装するには、CASL ライブラリを 2 回カプセル化する必要があります。必要なビジネスを作成するには、nest-cli を実行します。

ネスト g モジュール casl
ネスト g クラス casl/casl-ability.factory

ユーザーのオブジェクトを作成するには、CaslAbilityFactory の createForUser() メソッドを定義します。

type Subjects = InferSubjects<typeof Article | typeof User> | 'all'

エクスポートタイプ AppAbility = Ability<[Action, Subjects]>

@インジェクタブル()
CaslAbilityFactoryクラスをエクスポートします。
 ユーザーを作成します(ユーザー: ユーザー) {
 const { can, cannot, build } = new AbilityBuilder<
  能力<[アクション、対象]>
 >(Ability を AbilityClass<AppAbility> として)

 (ユーザー.isAdmin){
  can(Action.Manage, 'all') // すべての読み取りおよび書き込み操作を許可する } else {
  can(Action.Read, 'all') // 読み取り専用操作}

 できます(Action.Update、記事、{authorId:user.id})
 できません(Action.Delete、Article、{isPublished: true})

 ビルドを返す({
  // 詳細: https://casl.js.org/v5/en/guide/subject-type-detection#use-classes-as-subject-types
  対象タイプを検出: item => item.constructor を ExtractSubjectType<Subjects> として
 })
 }
}

次に、それを CaslModule にインポートします。

'@nestjs/common' から { モジュール } をインポートします。
'./casl-ability.factory' から { CaslAbilityFactory } をインポートします。

@モジュール({
 プロバイダー: [CaslAbilityFactory],
 エクスポート: [CaslAbilityFactory]
})
CaslModule クラスをエクスポートします {}

次に、CaslModule を任意のビジネスにインポートし、コンストラクターに挿入して使用します。

コンストラクター(プライベート caslAbilityFactory: CaslAbilityFactory) {}

定数 能力 = this.caslAbilityFactory.createForUser(ユーザー)
(能力がAction.Read、'すべて')の場合){
 // "user" はすべてのコンテンツを読み書きできます}

現在のユーザーが通常の権限を持つ管理者以外のユーザーである場合、記事を読むことはできますが、新しい記事を作成したり、既存の記事を削除したりすることはできません。

定数ユーザー = 新しいユーザー()
ユーザー.isAdmin = false

定数 能力 = this.caslAbilityFactory.createForUser(ユーザー)
能力.can(Action.Read, Article) // true
能力.can(Action.Delete, Article) // false
能力.can(Action.Create, Article) // false

これは明らかに問題があります。現在のユーザーが記事の著者である場合、次の操作を実行できるはずです。

定数ユーザー = 新しいユーザー()
ユーザーID = 1

const article = 新しい Article()
記事の著者ID = ユーザーID

定数 能力 = this.caslAbilityFactory.createForUser(ユーザー)
ability.can(Action.Update, article) // true

記事の著者ID = 2
ability.can(Action.Update, article) // false

0x4 警察ガード

上記の単純な実装では、複雑なシステムのより複雑な要件を満たすことができないため、前回の認証の記事を使用してクラスレベルの承認戦略モードを拡張し、元の CaslAbilityFactory クラスを拡張します。

'../casl/casl-ability.factory' から { AppAbility } をインポートします

インターフェース IPolicyHandler {
 ハンドル(能力: AppAbility): ブール値
}

タイプ PolicyHandlerCallback = (ability: AppAbility) => ブール値

エクスポート型 PolicyHandler = IPolicyHandler | PolicyHandlerCallback

各ルーティング コントローラーでのポリシー チェックのためのサポート オブジェクトと関数 (IPolicyHandler および PolicyHandlerCallback) を提供します。

次に、特定のリソースに対して指定されたアクセス ポリシーを実行するための @CheckPolicies() デコレータを作成します。

エクスポート const CHECK_POLICIES_KEY = 'check_policy'
エクスポート const CheckPolicies = (...handlers: PolicyHandler[]) => SetMetadata(CHECK_POLICIES_KEY, handlers)

ルーティング コントローラにバインドされているすべてのポリシーを抽出して実行する PoliciesGuard クラスを作成します。

@インジェクタブル()
エクスポートクラスPoliciesGuardはCanActivateを実装します{
 コンストラクタ(
 プライベートリフレクター:リフレクター、
 プライベート caslAbilityFactory: CaslAbilityFactory、
 ){}

 非同期canActivate(コンテキスト: ExecutionContext): Promise<boolean> {
 定数ポリシーハンドラ =
  this.reflector.get<ポリシーハンドラ[]>(
  CHECK_POLICIES_KEY、
  コンテキスト.getHandler()
  ) || []

 const { ユーザー } = context.switchToHttp().getRequest()
 定数 能力 = this.caslAbilityFactory.createForUser(ユーザー)

 戻り値 policyHandlers.every((handler) =>
  this.execPolicyHandler(ハンドラ、機能)
 )
 }

 プライベート execPolicyHandler(ハンドラー: PolicyHandler、機能: AppAbility) {
 if (ハンドラの型 === '関数') {
  ハンドラを返す(機能)
 }
 handler.handle(ability) を返す
 }
}

request.user にユーザー インスタンスが含まれていると仮定すると、policyHandler はデコレータ @CheckPolicies() を介して割り当てられ、aslAbilityFactory#create を使用して Ability オブジェクト メソッドを構築し、ユーザーが特定のアクションを実行するのに十分な権限を持っているかどうかを確認し、このオブジェクトをポリシー処理メソッド (実装関数またはクラス IPolicyHandler のインスタンス) に渡して、ブール値を返す handle() メソッドを公開します。

@得る()
@UseGuards(ポリシーガード)
@CheckPolicies((ability: AppAbility) => ability.can(Action.Read, Article))
すべて検索() {
 this.articlesService.findAll() を返す
}

IPolicyHandler インターフェイス クラスを定義することもできます。

ReadArticlePolicyHandlerクラスをエクスポートし、IPolicyHandlerを実装します。
 ハンドル(能力: AppAbility) {
 能力を返すことができます(Action.Read, Article)
 }
}

次のように使用します。

@得る()
@UseGuards(ポリシーガード)
@CheckPolicies(新しい ReadArticlePolicyHandler())
すべて検索() {
 this.articlesService.findAll() を返す
}

Nest.js 認可検証方法の例に関するこの記事はこれで終わりです。Nest.js 認可検証に関するその他の関連コンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Nest.js 環境変数の設定とシリアル化の詳細な説明
  • expressを使用して複数の静的ディレクトリを提供するためにnest.jsを使用する方法

<<:  Windows での MySQL5 グリーン バージョンのインストールの概要 (推奨)

>>:  VmWareでcentos7をインストールするときにインターネットにアクセスできない問題の解決策

推薦する

CentOS の環境変数と設定ファイルの詳細な説明

序文CentOS 環境変数設定ファイル システムは階層型システムであり、他のマルチユーザー アプリケ...

MySQLの指定順序ソートクエリについての簡単な説明

最近、空港や駅でフライト情報を表示するものと似た大型スクリーンディスプレイのプロジェクトに取り組んで...

ハイパーリンクを使用してリンクファイルを開く HTML 方式の紹介

a および href 属性 HTML では、英語ではアンカーと呼ばれるハイパーリンクを表すために &...

Linux Centos8 CA証明書作成チュートリアル

必要なファイルをインストールする Yum インストール openssl-* -yデータベースインデッ...

SQL インジェクションのある Web サイトを見つける方法 (必読)

方法 1: Google の詳細検索を使用します。たとえば、次に示すように.asp?id=9などの ...

スライドボタン効果を実現するネイティブJS

Jsで作ったスライドボタンの具体的なコードは参考までに。具体的な内容は以下のとおりですまずエフェク...

JS オブジェクト コンストラクター Object.freeze

目次概要例1) オブジェクトをフリーズする2) 配列をフリーズする3) 浅い凍結4) ディープフリー...

Tomcatはスレッドプールを使用してリモート同時リクエストを処理します。

Tomcatが同時リクエストを処理する方法を理解することで、スレッドプール、ロック、キュー、および...

...

JS配列インデックス検出におけるデータ型の問題の詳細な説明

WeChat アプレット プロジェクトを書いていたとき、その中に「都市選択」機能がありました。作者は...

Linux スケジュールタスクの関連操作の概要

皆様の参考と操作を容易にするために、様々な主要ウェブサイトを検索し、関連するスケジュールされたタスク...

vue-nuxt ログイン認証の実装

目次導入リンク始めるコードを読み進めてくださいプロキシ設定傍受を要求する異なるプレフィックスを持つイ...

Tomcat を再デプロイした後にイメージやその他のリソースが自動的に削除される問題を解決します

昨日は写真をアップロードしてリンクを返す機能を実装していました。プロジェクトが Tomcat に再デ...

Docker での MySQL 8.0.20 のインストールと設定のチュートリアル

Dockerは参考までにMySQLバージョン8.0.20をインストールします。具体的な内容は以下のと...

HTMLにおける絶対パスと相対パスの違いの分析

図に示すように: 1 つのページには多数のファイルが接続されているため、ファイルを参照するときには、...