Help us understand the problem. What is going on with this article?

[ASP.NET Core 2.1]カスタムポリシーの作成

More than 1 year has passed since last update.

はじめに

ポリシーベースの承認やその使い方については他に詳しいサイトがたくさんあるので、軽く実装例を載せてみたいと思います。

ポリシーの定義

例として、あるタスクの作成者しかアクセスできないポリシーを作成します。

TaskAuthorOnly.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace TestWebSystem.Policy
{
    public class TaskAuthorOnlyRequirement : IAuthorizationRequirement
    {

    }

    public class TaskAuthorOnlyHandler : AuthorizationHandler<TaskAuthorOnlyRequirement>
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                       TaskAuthorOnlyRequirement requirement)
        {
            if (!context.User.HasClaim(c => c.Type == ClaimTypes.NameIdentifier))
            {
                return Task.CompletedTask;
            }

            if (context.Resource is FilterContext filterContext)
            {
                // コントローラで使用されるときはこっちが呼び出される。
                var routeValues = filterContext.RouteData.Values;
                if (((string)routeValues["controller"]) == "Tasks" && routeValues.ContainsKey("id"))
                {
                    var dbContex = ApplicationDbContext.GetDbContext();
                    var userId = context.User.FindFirst(c => c.Type == ClaimTypes.NameIdentifier).Value;
                    var taskId = (string)routeValues["id"];

                    var task = dbContex.Tasks.Find(taskId);

                    if (task != null && task.AuthorId == userId)
                    {
                        context.Succeed(requirement);
                    }
                }
            }
            else if (context.Resource is Task task)
            {
                // ビューで使用するときは、Resourceにビューモデルを与えるのでこっちが呼び出される。
                var userId = context.User.FindFirst(c => c.Type == ClaimTypes.NameIdentifier).Value;
                if (task.AuthorId == userId)
                {
                    context.Succeed(requirement);
                }
            }

            return Task.CompletedTask;
        }
    }
}

これをStartup.csに登録してあげます。

Startup.cs
services.AddAuthorization(options =>
{
    options.AddPolicy("TaskAuthorOnly", policy =>
        policy.Requirements.Add(new TaskAuthorOnlyRequirement()));
});

services.AddSingleton<IAuthorizationHandler, TaskAuthorOnlyHandler>();

余談:ポリシー内でApplicationDbContextを取得するには?

独自定義のポリシー内でApplicationDbContextをDIから取得する方法が見つからなかったので、下記のようなコードを追加してお茶を濁しました。
ご存じの方がいたら教えてください...。

Startup.cs
ApplicationDbContext.ConnectionString = Configuration.GetConnectionString("DefaultConnection");

services.AddDbContext<ApplicationDbContext>(options =>
    options
    .UseLazyLoadingProxies()
    .UseNpgsql(ApplicationDbContext.ConnectionString)
);
ApplicationDbContext.cs
public static string ConnectionString { get; set; }

public static ApplicationDbContext GetDbContext()
{
    var option = new DbContextOptionsBuilder<ApplicationDbContext>()
        .UseLazyLoadingProxies()
        .UseNpgsql(ConnectionString)
        .Options;

    var context = new ApplicationDbContext(option);
    context.Database.OpenConnection();
    context.Database.EnsureCreated();

    return context;
}

ポリシーの利用

コントローラでの利用

単にアノテーションを付けるだけです。

[Authorize(Policy = "TaskAuthorOnly")]
public async Task<IActionResult> Edit(

ビューでの利用

次のような感じで利用します。

@using Microsoft.AspNetCore.Authorization
@inject IAuthorizationService AuthorizationService

@if ((await AuthorizationService.AuthorizeAsync(User, Model, "TaskAuthorOnly")).Succeeded)
{

}

おわり

以上、カスタムポリシーを定義して利用する方法でした。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away