現在のプロジェクトにはReport
クラスがあり、そのためのサービスレイヤーを実装します。すべてのメソッドは、一部のロールに対してのみ許可されます。このような:
public class ReportService : IReportService
{
public void CreateReport(Report report, User user)
{
if(user.HasRole("Admin"))
{
throw new SecurityException("You do not have permissions to do it");
}
...
}
public void ModifyReport(Report report, User user)
{
if(user.HasRole("Admin"))
{
throw new SecurityException("You do not have permissions to do it");
}
...
}
public bool ValidateReport(Report report, User user)
{
if (!user.HasRole("Manager"))
{
throw new SecurityException("You do not have permissions to do it");
}
}
public void DeleteReport(Report report, User user)
{
if (user.HasRole("Employee") || user.HasRole("Manager"))
{
throw new SecurityException("You do not have permissions to do it");
}
...
}
}
この簡単なアプローチは大丈夫ですか?もっと良いアイデアはありますか?
デコレータパターン として実装するアイデアがあるので、コードは次のようになります。
public class ReportService : IReportService
{
public void CreateReport(Report report, User user)
{
// Actual implementation
}
public void ModifyReport(Report report, User user)
{
// Actual implementation
}
public bool ValidateReport(Report report, User user)
{
// Actual implementation
}
public void DeleteReport(Report report, User user)
{
// Actual implementation
}
}
public class PermissionsReportService : IReportService
{
private IReportSerivce _service;
public PermissionsReportService(IReportService service)
{
_service = service;
}
public void CreateReport(Report report, User user)
{
if (user.HasRole("Admin"))
{
throw new SecurityException("You do not have permissions to do it");
}
_service.CreateReport(report, user);
}
public void ModifyReport(Report report, User user)
{
if (user.HasRole("Admin"))
{
throw new SecurityException("You do not have permissions to do it");
}
_service.ModifyReport(report, user);
}
public bool ValidateReport(Report report, User user)
{
if (!user.HasRole("Manager"))
{
throw new SecurityException("You do not have permissions to do it");
}
_service.ValidateReport(report, user);
}
public void DeleteReport(Report report, User user)
{
if (user.HasRole("Employee") || user.HasRole("Manager"))
{
throw new SecurityException("You do not have permissions to do it");
}
_service.DeleteReport(report, user);
}
}
役割のアクセス許可のチェックとその他のロジックが分割されているため、より良いように見えませんか?
私はまた別の懸念を持っています。ほとんどの場合、このサービスは ASP.NET MVC プロジェクトで使用され、Microsoftは次のようなさまざまな役割のアクセス許可にAuthorize
属性を使用することを提案しています
public class ReportController : Controller
{
[HttpGet]
[Authorize(Roles="Admin")]
public void DeleteReport(int reportId)
{
}
}
このアプローチを使用する場合、ロールの権限ロジックを複製します(サービスレイヤー内とコントローラー内)。私は何をすべきか?マイクロソフトのアプローチを拒否する必要がありますか?または、サービス層でロールの権限チェックを削除する必要がありますか(この場合、サービスが(たとえば)コンソールアプリケーションで使用される場合はどうすればよいですか?)または、このコードを複製して、サービス層とWeb層で権限チェックを行っても問題ありませんか?
私は、すべての権限を処理するクラスを優先して両方を捨てると言います。次に、クラスはチェックする権限の文字列識別子を渡し、クラスはルックアップを実行して、そのアクションを実行するために必要なロールを確認します。
たとえば、次のようなメソッドではありません。
public void CreateReport(Report report, User user)
{
if(user.HasRole("Admin"))
{
throw new SecurityException("You have not permissions to do it");
}
...
}
私はもっともっと何かを期待します:
public void CreateReport(Report report, UserAuth auth)
{
auth.Validate("report.create"); // throws SecurityException if invalid
...
}
UserAuthクラスはUserインスタンスを受け取り、ユーザーがそのアクションを実行するために必要なロールを持っていない場合、SecurityExceptionをスローします。理想的には、アクションと必要な役割の間のリンクは、データベースから動的にロードされますが、単純なディクショナリから始めることもできます。
私はまだファサードを使用して、実際の権限チェックと実際のアクションを分離します。これは、私の考えでは、それは懸念の分離であり、まったく同じ別のクラスに保持する必要があるためです。
あなたのコントローラーも同じことをします。重要なのは、アクセス許可の背後にあるロジックが独自のクラスにあることです。あなたはまだそれを呼び出す必要がありますが、ルックアップしようとしているパーミッションが呼び出されているもの以外は何も気にする必要はありません。たとえば、そのようなチェックに値するすべてのメソッド呼び出しに対して、ユーザーが従業員または管理者の役割を持っているかどうかをチェックすることは、一種のアンチパターンです。
お役に立てば幸いです。