質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.79%

ASP.NET COREでロールの確認(IsInRoleAsync)をするタイミング

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 477

score 32

前提・実現したいこと

ASP.NET COREで画面表示などのタイミングで頻繁にロールの確認を行います。
複数の画面がある場合に、その都度IsInRoleAsyncを行うべきで、個々の関数に入る単位で実行するものなのでしょうか?
public async Task<IActionResult> OnGetAsync()や、
public async Task<IActionResult> OnPostAsync()単位でその都度チェックするべきでしょうか?

具体的にやろうとしていること

  • 利用ユーザ、店舗オーナ、管理者の3種類のロールがあります。
  • 店舗は、1つの店舗情報に対してイベントを複数作る事ができます。
  • 匿名または利用ユーザは、店舗情報とイベントを見る事ができます。
  • 店舗オーナは、自分の店舗とイベントの情報の変更ができますが、他オーナのは変更できません(利用ユーザと同じ権限)
  • 管理者は、全部の店舗情報、イベントの変更ができます。

例えば、
「ユーザ詳細情報」というのを表示すると、
利用ユーザの場合は自分のプロフィールが見れます。
利用ユーザの場合は、店舗情報の項目は表示されません。
店舗オーナの場合は、所有する店舗の情報が追加で表示されます。
管理者以外は他人の「ユーザ詳細情報」の表示はできません。

他にも、serIDの値を店舗オーナーの場合は、自分のUserIDのみ可。管理者ならどのIDでも可という様にロールによって可能な範囲が変わる使い方を考えています。
これらの制御をロールで行いたいのですが、毎回IsInRoleAsync()で取得をするものなのか?が知りたいです。

↓今は、こういう感じで動作させています。
イメージ説明

@if (Model.OwnerFlg)
{
  <div>
      <h4>■所有店舗情報(StoreTbl)</h4>
      <table class="table">
          <thead>
              <tr>
                  <th>
                      @Html.DisplayNameFor(model => model.StoreTbls[0].StoreTblId)
                  </th>
                  <th>
                      @Html.DisplayNameFor(model => model.StoreTbls[0].OwnerTblId)
        public bool AdminFlg;
        public bool OwnerFlg;

        public async Task<IActionResult> OnGetAsync(string id)
        {
            AdminFlg = false;
            OwnerFlg = false;

            var user = await _userManager.GetUserAsync(User);
            if (user == null)
            {
                //存在しないユーザは表示できない
                return NotFound($"ユーザが存在しません ID = '{_userManager.GetUserId(User)}'.");
            }

            //ロール情報の取得
            if (await _userManager.IsInRoleAsync(user, "Admin") == true) { AdminFlg = true; }
            if (await _userManager.IsInRoleAsync(user, "Owner") == true) { OwnerFlg = true; }

            //id指定が無い場合は自分を表示
            if (id == null) { id = user.Id; }

            //管理者でなければ他人の情報は見れない
            if (AdminFlg == false)
            {
                if (id != user.Id)
                {
                    return NotFound($"他人の情報は見れません ID = '{_userManager.GetUserId(User)}'.");
                }
            }

            //ユーザのプロフィールを取得
            App01StoreFindUser = await _context.App01StoreFindUser.FirstOrDefaultAsync(m => m.Id == id);
            if (App01StoreFindUser == null)
            {
                return NotFound();
            }

            //オーナーの場合、所有する店の一覧を表示する
            if (OwnerFlg == true)
            {
                var stores = await _datacontext.StoreTbl.
                        OrderBy(stores => stores.StoreTblId).ToListAsync();
            }

自分で考えたり試した方法

  1. 関数(OnGetAsyncなど)の呼び出される都度、if (await _userManager.IsInRoleAsync(user, "Admin") == true) { }
    を実行して正常に動作する事は確認できています。
    ※毎回確認は必要だが、実現方法として無駄ではないか?

  2. 認証でログインする時点(Login.cshtml.cs)で、チェックをしセッション変数に入れる。ログアウト時にセッション変数から削除する。日頃はこのセッション変数を使用するという方法。
    ※ログイン中に他人にロールが変更されるのは無視していいが、セッション変数が有効期限切れになった場合はどうするか?

  3. 全てのケースにおいて通過する関数があれば、そこにセッション変数がまだ有効かを確認する処理を入れ、期限切れになっていれば、IsInRoleAsync()で再取得を行い、セッション変数を再セットするという方法。
    毎回必ず通過する関数はあるのでしょうか?自分で確認したのですが、Program.csやStartup.csは起動時に1回だけ実行されるだけなのですね・・・

気にしていること確認したいこと

  • ロールを使用する場合、世間一般にはどういう様に実装しているのか?
    ロールチェックやセッション変数の使い方が分っていても、実際の具体的な実装方法が知りたいです。
  • 「IsInRoleAsync()を毎回使う」のと、「その結果をセッション変数に入れておく」方法のコスト差(主に実行時の速度パフォーマンス)が気になります。
    DBとのデータアクセスの方が遥かにコストが高いので気にする事は無いのかもしれませんが
  • リスト1つの関数内であれば、関数の先頭で取得して、AdminFlgを覚えておけば良いですが、
    ページが複数あるのでそれぞれの、ユーザの情報や、店舗情報、イベントのページでGetやPostが呼ばれる毎に毎回IsInRoleAsync()で取得するのは、当然の事なのでしょうか?
    これらの処理を行う共通関数を用意して、OnGetなどの各関数の先頭でuserManager、User、AdminFlg、OwnerFlgを引数にして呼ぶ・・・というよりは、これらの要素を持ったクラスを生成する?のが良いのでしょうか?

補足情報(FW/ツールのバージョンなど)

ASP.NET CORE razore

解決した内容のまとめ

  • User (ClaimsPrincipal) は必ず生成されるので、そこにあるIsInRole()を使えばよい。
  • 認証クッキーに含まれる認証チケットから生成するのでDBアクセスは行われない為、パフォーマンスは気にしなくて良い。
  • ↑上記2つに事から、コード中やhtmlの中でその都度IsInRole()で判定する方法で良い
  • ページ全体をロールの判断で制御する場合は[Authorize(Roles = "Administrator")] の方法が良い(オマケ)
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

そもそも何をしたいのか分かりません。それによって答えが変わってくると思うのですが、普通にロールを使う話としてレスします。

あるページへのロールによるアクセス制限がしたいということなら、ページのクラスに Authorize 属性を付与すれば「ロールの確認(IsInRoleAsync)をするタイミング」だとか「チェックをしセッション変数に入れる」とかに悩む必要はありません。

例えば Administrator というロールがあって、Administrator ロールを持つユーザー以外のページへのアクセスを制限したいなら、そのページのクラスに以下のように属性を付与すれば OK です。お試しください。

[Authorize(Roles = "Administrator")]
public class PrivacyModel : PageModel
{
}

Razor ASP.NET Core でのページ承認規則
https://docs.microsoft.com/ja-jp/aspnet/core/security/authorization/razor-pages-authorization

以下抜粋(翻訳がアレなので英文を載せておきます):

"注意 An AuthorizeFilter can be applied to a page model class with the [Authorize] filter attribute. For more information, see Authorize filter attribute."

ASP.NET Core でのロールベースの承認
https://docs.microsoft.com/ja-jp/aspnet/core/security/authorization/roles

残念ながら OnGetAsync() や OnPostAsync() 単位には Authorize 属性は適用できないそうです。

ASP.NET Core での単純な承認
https://docs.microsoft.com/ja-jp/aspnet/core/security/authorization/simple

上記のような話ではないなら、そもそも何がしたいのかのシナリオ・目的などを書いてください。

【追記】

ロールを使用する場合、世間一般にはどういう様に実装しているのか? ロールチェックやセッション変数の使い方が分っていても、実際の具体的な実装方法が知りたいです。

「世間一般には」というと、自分が知る限りでは上の回答に書いた通りアクセスの制限で、[Authorize(Roles = "Administrator")] というような属性を付与することです。

ロールによって同じページの中で処理を分けるというのは自分の想像の範囲を超えてました。

「IsInRoleAsync()を毎回使う」のと、「その結果をセッション変数に入れておく」方法のコスト差(主に実行時の速度パフォーマンス)が気になります。DBとのデータアクセスの方が遥かにコストが高いので気にする事は無いのかもしれませんが

IsInRoleAsync() メソッドを使うとそのたび DB にアクセスするが、それを避けるために Session を使うということを考えているのですよね。

上の回答で紹介した記事、

ASP.NET Core でのロールベースの承認
https://docs.microsoft.com/ja-jp/aspnet/core/security/authorization/roles

に書いてあるように、ClaimsPrincipal クラスの IsInRole メソッドを使ってください。以下のような感じ。

イメージ説明

質問者さんの先のスレッド https://teratail.com/questions/321876 で言いましたが、User で取得できるのは ClaimsPrincipal です。なので、OnGetAsync メソッドの中で上のようにして ClaimsPrincipal.IsInRole メソッドで判定できます。

User すなわち ClaimsPrincipal は認証クッキーに含まれる認証チケットから生成するので、いちいち DB にアクセスしに行ってません。なので「その結果をセッション変数に入れておく」うんぬんは全く考える必要はありません。

リスト1つの関数内であれば、関数の先頭で取得して、AdminFlgを覚えておけば良いですが、ページが複数あるのでそれぞれの、ユーザの情報や、店舗情報、イベントのページでGetやPostが呼ばれる毎に毎回IsInRoleAsync()で取得するのは、当然の事なのでしょうか? これらの処理を行う共通関数を用意して、OnGetなどの各関数の先頭でuserManager、User、AdminFlg、OwnerFlgを引数にして呼ぶ・・・というよりは、これらの要素を持ったクラスを生成する?のが良いのでしょうか?

上に述べた通りです。User (ClaimsPrincipal) は必ず生成されますので、それから IsInRole メソッドでロールに属するか否かを判定してください。

イメージ説明

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/02/28 18:08

    なんどもすみません、追記しました

    キャンセル

  • 2021/02/28 20:06

    お手数でした。回答欄に追記しておきます。

    キャンセル

  • 2021/03/01 13:11

    User (ClaimsPrincipal) にIsInRole があるのを見落としていたというか、理解が不十分で回りくどく考えていたようでした。
    ASP.NETの全容の理解が全然足りてないのですね。
    お恥ずかしい限りです。
    ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.79%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る