web-dev-qa-db-ja.com

AspNetUserRolesテーブルを参照せずに、Web APIメソッド内でユーザーロールを取得するにはどうすればよいですか?

ステータスを更新するストアドプロシージャがあります。ユーザーの役割に応じて、ストアドプロシージャには、ステータスの変更を許可するコードと許可しないコードがあります。このため、ストアドプロシージャにロール名を渡す必要があります。私のロール名はJavaScriptコードでクライアントに保存されていますが、もちろんサーバーで2回目のチェックが必要です。各ユーザーは3つの役割の1つだけを持ち、ステータスの更新を要求するときに、クライアントの役割に応じて3つのメソッドの1つを呼び出すことができます。これが私が試したものです。

私はベアラートークンベースの認証とASP.NET Identity 2.1を使用したWebApiを使用しており、アプリケーションは常にブラウザーで実行されます。ユーザーには適切な役割が設定されています。

UserIdを取得するためのコードを配置し、AspNetUserRolesテーブルに移動して、メソッドの開始時にロールを取得します。ただし、実行に約500ミリ秒かかることに気づきました。別の方法として、以下を検討しています。

    [HttpPut]
    [Authorize(Roles = "Admin")]
    [Route("AdminUpdateStatus/{userTestId:int}/{userTestStatusId:int}")]
    public async Task<IHttpActionResult> AdminUpdateStatus(int userTestId, int userTestStatusId)
    {
        return await UpdateStatusMethod(userTestId, userTestStatusId, "Admin");
    }

    [HttpPut]
    [Authorize(Roles = "Student")]
    [Route("StudentUpdateStatus/{userTestId:int}/{userTestStatusId:int}")]
    public async Task<IHttpActionResult> StudentUpdateStatus(int userTestId, int userTestStatusId)
    {
        return await UpdateStatusMethod(userTestId, userTestStatusId, "Student");
    }

    [HttpPut]
    [Authorize(Roles = "Teacher")]
    [Route("TeacherUpdateStatus/{userTestId:int}/{userTestStatusId:int}")]
    public async Task<IHttpActionResult> TeacherUpdateStatus(int userTestId, int userTestStatusId)
    {
        return await UpdateStatusMethod(userTestId, userTestStatusId, "Teacher");
    }

    private async Task<IHttpActionResult> UpdateStatusMethod(int userTestId, int userTestStatusId, string roleName)
    {
        // Call the stored procedure here and pass in the roleName
    }

これはこれを行うための効率的な方法ですか、それともおそらくもっとクリーンな方法がありますか?フロントエンドまたはバックエンドがユーザーの役割をキャッシュするかどうかは、はっきりしていません。私はこれが行われたか、またはこれを可能にするいくつかの設定があると想定しています。

注ここでは、クレームを使用してロール情報をクライアントに送信しています。

public static AuthenticationProperties CreateProperties(
            string userName,
            ClaimsIdentity oAuthIdentity,
            string firstName,
            string lastName,
            int organization)
        {
            IDictionary<string, string> data = new Dictionary<string, string>
                {
                    { "userName", userName},
                    { "firstName", firstName},
                    { "lastName", lastName},
                    { "organization", organization.ToString()},
                    { "roles",string.Join(":",oAuthIdentity.Claims.Where(c=> c.Type == ClaimTypes.Role).Select(c => c.Value).ToArray())}

                };
            return new AuthenticationProperties(data);
        }

ただし、ここでの質問はサーバーに関するものであり、ユーザーがデータベースにアクセスせずに特定のロールを持っているかどうかをメソッドにチェックインする方法に関するものです。クレームでこれを安全に行う方法があるかもしれませんが、それを行う方法がわかりません。

どんな助けとアドバイスでも大歓迎です。

25

あなたが述べたように、あなたはあなたのエンドポイントを保護するために無記名トークンを使用しています。私はそれらの無記名トークンの魔法の文字列がその中に何を含んでいるかについて誤解がないと信じています。これらのトークンには、トークンを発行したユーザーのすべてのロールが含まれています。また、Web APIではなくデフォルトのデータ保護DPAPI(JWTトークン)を使用している場合、これらのトークンは署名および暗号化されているため、誰もデータを改ざんできませんトークンの内部では、WebサーバーのmashineKeyがこのトークンを発行していない限り、データ保護について心配する必要はありません。

私の推奨事項は、データベースからユーザーのロール/クレームを読み取ることです。この回避策やハックを行う必要はありません。ユーザーがメソッドGrantResourceOwnerCredentialsにログインするときに、ユーザーのクレームを設定するだけです。このように設定するには、ユーザーを取得し、DBからロールを読み取って、「ロール」タイプのクレームとして設定します。

_ var identity = new ClaimsIdentity(context.Options.AuthenticationType);
 identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
 identity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
 identity.AddClaim(new Claim(ClaimTypes.Role, "Supervisor"));
_

これが発生するのは、ユーザーがログインしたときに1回だけであることに注意してください。その後、このユーザーのすべての要求を含む署名付きの暗号化された署名なしのトークンを受け取ります。DBにアクセスして確認する必要はありません。

または、データベースからIDを作成する場合は、以下を使用できます。

_ public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, string authenticationType)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
        // Add custom user claims here
        return userIdentity;
    }
_

次に、GrantResourceOwnerCredentialsで以下を実行します。

_ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, OAuthDefaults.AuthenticationType);
_

これで、[Authorize(Roles = "Teacher")]などのAuthorize属性を持つ保護されたエンドポイントにベアラートークンを送信すると、コードがDBにアクセスしてクエリを実行しないことを保証できますSQLを開くプロファイラーとチェックは、暗号化されたトークンからクレームとロールクレームを読み取り、このユーザーがTeacherロールに属しているかどうかを確認し、リクエストを許可または拒否します。

Authorization server および JWT tokens に加えて、 Token Based Authentication に関する5つの一連の詳細なブログを投稿しました。ベアラートークンについて理解を深めるために、これらの投稿を読むことをお勧めします。

34
Taiseer Joudeh

3つの個別のメソッドを使用するのではなく、コントローラークラスで使用可能なUser.IsInRole("RoleName")を確認するだけです。 [Authorise]属性と同じロジックを使用しています。例えば.

public class DataApiController : ApiController
{
    [HttpPut]
    [Route("UpdateStatus/{userTestId:int}/{userTestStatusId:int}")]
    public async Task<IHttpActionResult> UpdateStatus(int userTestId, int userTestStatusId)
    {
        if(User.IsInRole("Admin"))
        {
            //Update method etc....
        }
        //else if(....) else etc
    {
}
10
Excommunicated

役割の要求はデフォルトでユーザーのログインCookieに保存されるため、[Authorize(Roles = "Teacher")]は高速である必要があります。唯一のデータベースヒットは、最初にサインインしたとき(またはサインインCookieが更新されたとき)です。承認チェックは、サインインCookieから作成されたIClaimsPrincipalに対して行われます。

これを機能させるには、他のコードも更新する必要がある場合があります。次の質問を参照してください。これは、実行しているものと同様です。 Authorize with Roles

2
Hao Kung