2016-03-04 11 views
6

Tôi thực hiện xác thực mô hình trong bộ điều khiển của mình, nhưng việc xác thực doanh nghiệp thứ hai cần phải diễn ra ở cấp dịch vụ/doanh nghiệp. Điều này thường liên quan đến quyền của người dùng: người dùng hiện tại có quyền truy cập vào thông tin khách hàng/đơn đặt hàng mà anh đang cố gắng nhận hoặc đăng không?Bao gồm người dùng hiện tại vào mọi cuộc gọi đến lớp dịch vụ từ bộ điều khiển

Cách tiếp cận đầu tiên (và làm việc) của tôi là vượt qua toàn bộ trường hợp User hoặc Id (bằng cách gọi User.Identity.GetUserId()), điều này sẽ là đủ nhất - không phải tất cả thời gian. Vì vậy, tôi sẽ có một cái gì đó như thế này:

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

Nhưng tôi không thực sự thích thực tế là với cách tiếp cận này tôi sẽ phải bao gồm một tham số thêm trong mọi cuộc gọi đến lớp dịch vụ của tôi. Nếu tôi đang gọi phương thức GetById(), tôi muốn nhận một cái gì đó bằng cách cung cấp ID, không phải ID người dùng ID ID người dùng.

Một cách giải quyết đơn giản sẽ là một cái gì đó dọc theo những dòng này, mà cũng làm việc:

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); 
    } 
} 

Nhưng thay vì phải thực hiện cuộc gọi riêng biệt để thiết lập người dùng hiện tại, tôi muốn điều này được thực hiện tự động với mọi cuộc gọi đến dịch vụ. Tôi làm nó như thế nào?

Đây là những gì dịch vụ của tôi trông giống như:

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); 
    } 
} 

Trả lời

1

Tôi thà thực hiện luận lý uỷ quyền này trong một bộ lọc cho phép tùy chỉnh. Bạn không cần phải tiếp cận mã hành động của bộ điều khiển nếu người dùng không được xác thực hoặc ủy quyền.

Ví dụ bạn có thể có một cái gì đó như thế này:

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 
    } 
} 

và sau đó bạn có thể trang trí bộ điều khiển hay hành động của bạn đòi hỏi phải có loại giấy phép với các thuộc tính tùy chỉnh:

[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); 
    } 
} 

Tất nhiên điều này thuộc tính tùy chỉnh có thể được cải thiện hơn nữa bằng cách sử dụng nhà cung cấp bộ lọc tùy chỉnh cho phép chèn các ngữ cảnh dữ liệu của bạn vào nó để bạn có thể thực hiện các cuộc gọi thích hợp. Sau đó, bạn chỉ có thể có một thuộc tính đánh dấu sẽ được nhà cung cấp bộ lọc sử dụng để quyết định xem nó có nên thực hiện logic ủy quyền hay không.

+0

Tôi đã sử dụng một 'CustomAuthorizeAttribute' để trả về một thông báo lỗi cụ thể trong trường hợp các mã thông báo là không hợp lệ hoặc người dùng không có vai trò cần thiết. Cách tiếp cận này có vẻ rất thú vị vì nó sẽ loại bỏ một * tấn * logic xác nhận trong lớp logic nghiệp vụ của tôi, nhưng nó cảm thấy hơi lạ khi có nhiều logic xác nhận ở đâu đó trong ứng dụng của tôi. Ngoài ra, bạn có thể mở rộng đoạn cuối của mình không? Tiêm bối cảnh dữ liệu của tôi (dịch vụ của tôi, trong trường hợp này) vào bộ lọc ủy quyền có vẻ như một vấn đề khác mà tôi cần phải giải quyết để sử dụng phương pháp này. – Antrim

+0

Như tôi đã nêu trong câu trả lời của tôi nếu bạn cần tiêm phụ thuộc, tất cả những gì bạn cần sử dụng là một nhà cung cấp bộ lọc tùy chỉnh. Vì vậy, sau một số tìm kiếm bạn có thể tìm thấy: http://haacked.com/archive/2011/04/25/conditional-filters.aspx/ Thuộc tính điểm đánh dấu trên các hành động của trình điều khiển có thể cho phép bạn phát hiện trong nhà cung cấp bộ lọc tùy chỉnh nếu bạn cần để áp dụng logic ủy quyền. Và bạn nên tự hỏi mình: Tôi có đang sử dụng lớp kinh doanh này * ở bất cứ đâu ngoài ứng dụng MVC mà tôi nên quan tâm không? Nếu có lẽ lớp kinh doanh này đã được bọc phía sau một mặt tiền RESTful. –

+0

Vì vậy, không chỉ cần chờ đợi tôi viết tất cả các mã cho bạn, nhìn xung quanh, thử nghiệm, trở lại với các câu hỏi cụ thể nếu bạn có như vậy. –

0

Dịch vụ của bạn có cần tính đến id người dùng khi thực hiện logic của nó không? Nếu vậy thì nó hợp lý để nó là một phần của đầu vào. Hoặc là nó chỉ là ủy quyền hơn, nơi mà các dịch vụ nên từ chối yêu cầu mà không cần xử lý tùy thuộc vào người dùng là ai (nhưng một khi được ủy quyền nó không quan trọng người dùng là ai)? Bạn có thể chèn additional data into the header of the WCF request và sau đó kiểm tra nó theo yêu cầu đến. Nhưng a) đó là một nỗi đau, và b) nó không hoàn toàn rõ ràng từ giao diện dịch vụ mà khách hàng nên cung cấp dữ liệu đó.

Vì lý do đó tôi sẽ đặt id người dùng vào đầu vào. Tôi sẽ không biến nó thành tài sản của dịch vụ. Nó thực sự là một thuộc tính của đầu vào, không phải lớp xử lý đầu vào đó.

An interceptor vẫn là một ý tưởng hay nếu bạn không muốn dịch vụ của mình có thêm trách nhiệm cho phép yêu cầu. Bằng cách đó bạn đặt một thuộc tính vào dịch vụ hoặc phương thức dịch vụ của bạn và giữ quyền trong bộ chặn.Nếu người dùng không thể gọi dịch vụ hoặc phương thức thì nó sẽ từ chối cuộc gọi trước khi nó đến lớp dịch vụ.

0

Câu trả lời trễ nhưng tôi hiểu mối quan tâm của bạn, thực hành tốt nhất bạn muốn đạt được trong mã của mình và làm cho nó sạch sẽ nhất có thể, tôi đã có cùng một vấn đề giống như của bạn từ lâu và giống như bạn cảm thấy rằng điều này không bên phải, có phải là một cách tốt hơn để làm điều đó, và trong trường hợp của tôi, tôi sử dụng trên tất cả các lớp của tôi:
System.Threading.Thread.CurrentPrincipal.Identity
và câu trả lời này trên SO mô tả nó: https://stackoverflow.com/a/27636802/20126

và trong khi đây là cách tôi luôn luôn sử dụng , cũng đáng xem xét những gì người khác đã làm:
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

Các vấn đề liên quan