2017-01-09 14 views
9

Xem xét việc sử dụng các hành vi đường ống tính năng mới của Mediatr 3.0 để xác thực/ủy quyền.Mediatr 3.0 Sử dụng hành vi đường ống để xác thực

Bạn có thường xác thực dựa trên thông báo hoặc trình xử lý không? lý do tôi hỏi là tôi muốn auth trên xử lý (giống như bộ điều khiển trong MVC) nhưng hành vi dường như không có kiến ​​thức về xử lý vì vậy tôi không chắc chắn điều này là có thể/phù hợp.

Tôi có thể thêm giao diện điểm đánh dấu IAuthorisationRequired vào mỗi thư, nhưng nếu thư là thông báo/sự kiện và có nhiều trình xử lý thì có thể một số sẽ chạy nhưng không nên chạy. Thực sự cảm thấy tốt hơn kiểm tra auth trên mã xử lý mà thực hiện công việc thực tế.

Rất thích có thể đặt thuộc tính [Ủy quyền] trên trình xử lý và hành vi của người dùng để kiểm tra (tôi hiện thực hiện chính xác điều này nhưng với lớp cơ sở thay vì hành vi).

public class AuthenticationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> 
{ 
    public Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next) 
    { 
     //Can't access handler class here, so how do I know the action requires authentication/authorization? 
     return next(); 
    } 
} 

[Authorize] 
public class ChangePasswordRequestHandler : IAsyncRequestHandler<ChangePassword, ReponseType> 
{ 
    protected override async Task<ReponseType> Handle(AsyncRequestBase<ChangePassword> message) 
    { 
     //change users password here 
    } 
} 
+0

Nhìn chung, tôi không hài lòng với bất kỳ giải pháp được đề xuất nào và đã kết thúc với giải pháp trước 3.0 của tôi. Ủy quyền phải được gắn với hành động được đề cập không phải là thông báo. – Betty

Trả lời

4

Bạn nói đúng, RequestDelegateHandler<TResponse> không hiển thị trình xử lý sẽ chạy tiếp theo và điều này là có chủ ý. Nếu bạn nghĩ về nó, đường ống trong MediatR 2.x sử dụng trang trí, và trong khi trang trí có quyền truy cập vào ví dụ của decorate, tôi sẽ tư vấn chống lại auth dựa trên nó. Lý do là nếu bạn cần trang trí ủy quyền của bạn để trang trí một trường hợp cụ thể của một trình xử lý - một trang trí với các thuộc tính cụ thể - thì chúng được ghép đôi, đánh bại mục đích của trang trí nơi bạn có thể đặt chúng trên đầu mỗi khác độc lập.

Đó là lý do tại sao tôi khuyên tư vấn cơ bản về thông báo, ít nhất trong hầu hết các trường hợp. Bạn có thể có một thiết kế có thể mở rộng trong đó mỗi thư được liên kết với một số quy tắc ủy quyền và hành vi sẽ đánh giá tất cả các quy tắc đó.

public interface IAuthorizationRule<TRequest> 
{ 
    Task Evaluate(TRequest message); 
} 

public class AuthorizationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> 
{ 
    private readonly IAuthorizationRule<TRequest>[] _rules; 

    public AuthorizationBehavior(IAuthorizationRule<TRequest>[] rules) 
    { 
     _rules = rules; 
    } 

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next) 
    { 
     // catch it or let it bubble up depending on your strategy 
     await Task.WaitAll(_rules.Select(x => x.Evaluate(request))); 

     return next(); 
    } 
} 

Đối với trường hợp cụ thể mà bạn đề cập đến nơi, đối với một thông báo, một số bộ xử lý có thể chạy trong khi những người khác không nên, bạn có thể luôn luôn sử dụng các hành vi uỷ quyền nhắm rằng thông điệp cụ thể và áp dụng chúng một cách chọn lọc để xử lý mà cần đến chúng . Tôi đoán điểm của tôi là bạn sẽ phải làm một chút của crafting mình khi bạn nhấn những kịch bản cụ thể.

+0

Thật không may tôi nghĩ bạn có thể đúng. Nhiều như tôi không thích làm auth dựa trên tin nhắn nó có thể là cách duy nhất với hành vi. bây giờ tôi nghĩ rằng tôi sẽ gắn bó với phương pháp trước 3.0 của tôi bằng cách sử dụng một lớp cơ sở và tìm kiếm [ủy quyền] thẻ. – Betty

+0

xin lỗi tôi đã bỏ lỡ cửa sổ để trao cho bạn toàn bộ tiền thưởng = ( – Betty

+0

haha ​​đừng lo lắng, vui mừng khi bạn tìm thấy câu trả lời hữu ích! –

1

Tôi có cùng yêu cầu cho một dự án và triển khai một đường ống cụ thể nơi tôi có thể tiêm (nếu cần) một AuthorisationHandler cho một yêu cầu cụ thể. Điều này có nghĩa là tôi chỉ cần thêm một AuthorisationHandler mới cho mỗi lệnh mới mà tôi đã tạo, và sau đó nó sẽ được gọi trước khi yêu cầu xử lý lệnh thực tế.

Đường ống dẫn dầu:

public class Pipeline<TRequest, TResponse> : IAsyncRequestHandler<TRequest, TResponse> where TRequest : IAsyncRequest<TResponse> 
{ 
    private readonly IAuthorisationHandler<TRequest, TResponse>[] _authorisationHandlers; 
    private readonly IAsyncRequestHandler<TRequest, TResponse> _inner; 
    private readonly IPostRequestHandler<TRequest, TResponse>[] _postHandlers; 

    public Pipeline(IAuthorisationHandler<TRequest, TResponse>[] authorisationHandlers, IAsyncRequestHandler<TRequest, TResponse> inner, IPostRequestHandler<TRequest, TResponse>[] postHandlers) 
    { 
     _authorisationHandlers = authorisationHandlers; 
     _inner = inner; 
     _postHandlers = postHandlers; 
    } 

    public async Task<TResponse> Handle(TRequest message) 
    { 
     foreach (var authorisationHandler in _authorisationHandlers) 
     { 
      var result = (ICommandResult)await authorisationHandler.Handle(message); 

      if (result.IsFailure) 
      { 
       return (TResponse)result; 
      } 
     } 

     var response = await _inner.Handle(message); 

     foreach (var postHandler in _postHandlers) 
     { 
      postHandler.Handle(message, response); 
     } 

     return response; 
    } 
} 

Các Authorsiation Handler:

public class DeleteTodoAuthorisationHandler : IAuthorisationHandler<DeleteTodoCommand, ICommandResult> 
{ 
    private IMediator _mediator; 
    private IAuthorizationService _authorisationService; 
    private IHttpContextAccessor _httpContextAccessor; 

    public DeleteTodoAuthorisationHandler(IMediator mediator, IAuthorizationService authorisationService, IHttpContextAccessor httpContextAccessor) 
    { 
     _mediator = mediator; 
     _authorisationService = authorisationService; 
     _httpContextAccessor = httpContextAccessor; 
    } 

    public async Task<ICommandResult> Handle(DeleteTodoCommand request) 
    { 
     if (await _authorisationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, "DeleteTodo")) 
     { 
      return new SuccessResult(); 
     } 

     var message = "You do not have permission to delete a todo"; 

     _mediator.Publish(new AuthorisationFailure(message)); 

     return new FailureResult(message); 
    } 
} 

AuthorisationHandler My implemements IAuthorisationHandler mà trông như thế này:

public interface IAuthorisationHandler<in TRequest, TResponse> where TRequest : IAsyncRequest<TResponse> 
{ 
    Task<TResponse> Handle(TRequest request); 
} 

Nó sau đó ăn khớp với nhau bằng cách sử dụng DecorateAllWith (phần của structuremap)

cfg.For(typeof(IAsyncRequestHandler<,>)).DecorateAllWith(typeof(Pipeline<,>)); 

Không chắc chắn bạn nên làm điều này cho 3.x như thế này bây giờ có một giao diện đường ống mới

IPipelineBehavior<TRequest, TResponse> 

Không sử dụng nó chưa nhưng tôi nghĩ rằng nó sẽ đơn giản hóa việc triển khai và có nghĩa là bạn có thể ngừng sử dụng Decorator Pattern DecorateAllWith.

+0

m tìm kiếm một vài khác biệt 1) cập nhật để sử dụng các hành vi mediatr 3.0 2) xác thực chung có thể hoạt động trên nhiều trình xử lý và một cách dễ dàng như MVC có thể chuyển xác thực theo yêu cầu cho trình xử lý đó (ví dụ: [Được ủy quyền]) – Betty

2

Bạn có thể làm điều này giống như cách tôi sử dụng Xác thực thông thạo.

tôi tạo ra các hành vi sau đây:

namespace MediatR.Extensions.FluentValidation 
{ 
    public class ValidationPipelineBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> 
    { 
     private readonly IValidator<TRequest>[] _validators; 

     public ValidationPipelineBehavior(IValidator<TRequest>[] validators) 
     { 
      _validators = validators; 
     } 

     public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next) 
     { 
      var context = new ValidationContext(request); 

      var failures = 
       _validators.Select(v => v.Validate(context)).SelectMany(r => r.Errors).Where(f => f != null).ToList(); 

      if (failures.Any()) 
      { 
       throw new ValidationException(failures); 
      } 

      return await next(); 
     } 
    } 
} 

Tạo một AbstractValidator

public classs SaveCommand: IRequest<int> 
{ 
    public string FirstName { get; set; } 

    public string Surname { get; set; } 
} 

    public class SaveCommandValidator : AbstractValidator<SaveCommand> 
    { 
     public SaveCommandValidator() 
     { 
      RuleFor(x => x.FirstName).Length(0, 200); 
      RuleFor(x => x.Surname).NotEmpty().Length(0, 200); 
     } 
    } 

Vì vậy, bạn có thể tạo ra một lớp Authorization<T> nơi bạn có thể thêm mã cho phép tùy chỉnh của bạn theo yêu cầu và đã tiêm vào một AuthorizationPipelineBehavior<TRequest, TResponse> lớp học.

+0

Tôi nghĩ rằng đây chính là câu trả lời tương tự Mickaël Derriey đã đưa ra, tôi có thiếu một số khác biệt không? – Betty

+0

'IValidator [] 'được tiêm vào hành vi như thế nào? – Shoe

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