2016-03-04 4 views
6

コントローラでモデル検証を実行しますが、サービス/ビジネスレベルで2番目のビジネス検証が必要です。これは通常、ユーザー権限に関係します。現在のユーザーは、取得または投稿しようとしている顧客/注文情報にアクセスできますか?コントローラからサービス層へのすべての呼び出しに現在のユーザーを含める

Userインスタンス全体(User.Identity.GetUserId()を呼び出すことによって)Idのいずれかを渡すのが最初の(やはり効果的な)アプローチです。これはすべてではありません。だから私はこのようなものがあります:

public IHttpActionResult Get(int id) 
{ 
    try 
    { 
     var customer = customerService.GetById(id, userId); 
     return Ok(customer); 
    } 
    catch (BusinessLogicException e) 
    { 
     return CreateErrorResponse(e); 
    } 
} 

をしかし、私は本当にこのアプローチで、私はかなりの私のサービス層へのすべての呼び出しを追加のパラメータを含めるする必要がありますするつもりだという事実が好きではありません。 GetById()メソッドを呼び出す場合は、ユーザーIDであるではなく、IDを指定して何かを取得したいと考えています。

簡単な回避策も働くこれらの線に沿って何か、次のようになります。

public IHttpActionResult Get(int id) 
{ 
    customerService.SetCurrentUser(User.Identity.GetUserId()); 

    try 
    { 
     var customer = customerService.GetById(id); 
     return Ok(customer); 
    } 
    catch (BusinessLogicException e) 
    { 
     return CreateErrorResponse(e); 
    } 
} 

しかし、その代わりに現在のユーザーを設定するための別の呼び出しを行うために持つのを、私はこれがで自動的に行うことがしたいのですがサービスへのすべての呼び出し。どうしたらいいですか?

public class CustomerService : EntityService<Customer>, ICustomerService 
{ 
    public string UserId; 
    IContext context; 
    public CustomerService(IContext context) : base(context) 
    { 
     this.context = context; 
     this.dbSet = context.Set<Customer>(); 
    } 

    public void SetCurrentUser(string userId) 
    { 
     UserId = userId; 
    } 

    public DTO.Customer GetById(int id) 
    { 
     if (!IsAccessibleByUser(id)) 
     { 
      throw new BusinessLogicException(ErrorCode.UserError, "UserId: " + UserId); 
     } 

     return dbSet.FirstOrDefault(x => x.Id == id).ToDto<Customer, DTO.Customer>(); 
    } 

    public bool IsAccessibleByUser(int id) 
    { 
     return context.UsersAPI.Any(a => a.AspNetUsersID == UserId); 
    } 
} 

答えて

1

が、私はむしろ、カスタム認証フィルタでは、この承認ロジックを実行します:

は、ここに私のサービスは次のようになります。ユーザーが認証されていない場合や許可されていない場合、コントローラのアクションコードに到達する必要はありません。

たとえば、あなたがこのようなものかもしれない:

public class MyAuthorizeAttribute : AuthorizeAttribute 
{ 
    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     var authorized = base.AuthorizeCore(httpContext); 
     if (!authorized) 
     { 
      return false; 
     } 

     var rd = httpContext.Request.RequestContext.RouteData; 
     // Get the id of the requested resource from the route data 
     string resourceId = rd.Values["id"] as string; 
     if (string.IsNullOrEmpty(resourceId)) 
     { 
      // No id of resource was specified => we do not allow access 
      return false; 
     } 

     string userId = httpContext.User.Identity.GetUserId(); 
     return IsAccessibleByUser(resourceId, userId); 
    } 

    private bool IsAccessibleByUser(string resourceId, string userId) 
    { 
     // You know what to do here => fetch the requested resource 
     // from your data store and verify that the current user is 
     // authorized to access this resource 
    } 
} 

をしてから、カスタム属性を使用して承認のこの種を必要とし、あなたのコントローラやアクションを飾ることができます:

[MyAuthorize] 
public IHttpActionResult Get(int id) 
{ 
    try 
    { 
     // At this stage you know that the user is authorized to 
     // access the requested resource 
     var customer = customerService.GetById(id); 
     return Ok(customer); 
    } 
    catch (BusinessLogicException e) 
    { 
     return CreateErrorResponse(e); 
    } 
} 

もちろん、このカスタム属性は、適切な呼び出しを実行できるようにデータコンテキストを挿入できるカスタムフィルタプロバイダを使用することでさらに改善できます。次に、承認ロジックを実行するかどうかを決定するために、フィルタプロバイダによって使用されるマーカー属性のみを持つことができます。

+0

トークンが有効でない場合、またはユーザーが必要な役割を持っていない場合には、私はすでに、特定のエラーメッセージを返すように 'CustomAuthorizeAttribute'を使用しています。このアプローチは、私のビジネスロジック層で検証ロジックの*トン*を取り除くので非常に興味深いようですが、私のアプリケーションのどこか他の場所で多くの検証ロジックを持つのはちょっと変です。それ以外に、あなたは最後の段落に展開できますか?私のデータコンテキスト(私のサービス、この場合)を認証フィルタに注入することは、このアプローチを使用するために解決しなければならない別の問題のように思えます。 – Antrim

+0

依存性注入が必要な場合は私の答えで既に述べたように、あなたが使用する必要があるのはカスタムフィルタプロバイダだけです。したがって、いくつかの検索の後で、あなたが見つけるかもしれません:http://haacked.com/archive/2011/04/25/conditional-filters.aspx/あなたのコントローラのアクションのマーカー属性は、必要に応じてカスタムフィルタプロバイダで検出することができます承認ロジックを適用します。そしてあなたは自分自身に尋ねるべきです:私は気にしなければならない私のMVCアプリケーションの外にこの*ビジネス層*を使用していますか?もしそうなら、おそらくこのビジネスレイヤーはすでにRESTfulファサードの後ろに包まれています。 –

+0

私はあなたのためにすべてのコードを書いて、周りを見て、実験して、特定の質問を持っている場合は戻ってくるのを待つだけではありません。 –

0

論理を実行する際にサービスがユーザーIDを考慮する必要がありますか?そうであれば、それが入力の一部であることは合理的です。 サービスは、ユーザーが誰であるかに応じて処理せずに要求を拒否する必要があります(ただし、承認されたユーザーは誰でも関係ありません)。 additional data into the header of the WCF requestを挿入して、着信要求を調べることができます。しかし、a)それは苦痛であり、b)クライアントがそのデータを提供しなければならないということは、サービスインターフェースから明らかではない。

そのため、私は入力にユーザーIDを入れます。私はそれをサービスの財産にしません。これは実際に入力のプロパティであり、その入力を処理するクラスではありません。

interceptorは、サービスがリクエストを承認する余分な責任を負うことを望まない場合には、まだまだ良い考えです。そのようにして、サービスまたはサービスメソッドに属性を設定し、インターセプタに認可を保持します。ユーザーがサービスまたはメソッドを呼び出せない場合、ユーザーはサービスクラスに到達する前に呼び出しを拒否します。

0

遅くとも、あなたのコードで達成したいと思っているベストプラクティスをできるだけきれいにしたいと思っています。私はまったく同じ問題を長い時間前と同じように感じていました。右、そこにそれを行うには良い方法であること、そして私の場合は私はすべての層で使用する必要があります
System.Threading.Thread.CurrentPrincipal.Identity
ので、それを記述する上でこの答え:https://stackoverflow.com/a/27636802/20126

、これは私がいつも使う方法ですが他の人たちが何をしているのか調べる価値があります。
Accessing HttpContext and User Identity from data layer
How to get User ID inside a separate assembly
Retrieving the current logged in user's Id in a class library outside of an ASP.NET Webforms App using forms authentication
Get the ID of the current user ASP.NET Membership

関連する問題