2012-09-27 31 views
29

Tôi đang kế thừa từ System.Web.Http.AuthorizeAttribute để tạo một ủy quyền tùy chỉnh/xác thực thường trình để đáp ứng một số yêu cầu bất thường cho một ứng dụng web được phát triển bằng ASP.NET MVC 4. Điều này bổ sung tính bảo mật cho Web API được sử dụng cho các cuộc gọi Ajax từ máy khách web. Các yêu cầu là:Làm thế nào để tùy chỉnh ASP.NET Web API AuthorizeAttribute cho các yêu cầu bất thường

  1. Người dùng phải đăng nhập mỗi khi họ thực hiện một giao dịch để xác minh ai đó đã không đi đến các máy trạm sau khi ai đó có đăng nhập và bỏ đi.
  2. Vai trò không thể được gán cho các phương thức dịch vụ web vào thời gian chương trình. Họ phải được chỉ định vào thời gian chạy để quản trị viên có thể định cấu hình điều này. Thông tin này được lưu trữ trong cơ sở dữ liệu hệ thống.

Máy khách web là single page application (SPA) để xác thực biểu mẫu điển hình không hoạt động tốt, nhưng tôi đang cố gắng sử dụng lại nhiều khuôn khổ bảo mật ASP.NET như tôi có thể để đáp ứng các yêu cầu. Tùy chỉnh AuthorizeAttribute hoạt động tốt cho yêu cầu 2 khi xác định vai trò nào được liên kết với phương thức dịch vụ web. Tôi chấp nhận ba tham số, tên ứng dụng, tên tài nguyên và hoạt động để xác định các vai trò nào được liên kết với một phương thức.

public class DoThisController : ApiController 
{ 
    [Authorize(Application = "MyApp", Resource = "DoThis", Operation = "read")] 
    public string GetData() 
    { 
     return "We did this."; 
    } 
} 

tôi ghi đè lên các phương pháp OnAuthorization để có được những vai trò và xác thực người dùng. Vì người dùng phải được xác thực cho mỗi giao dịch, tôi giảm bớt cuộc trò chuyện qua lại bằng cách thực hiện xác thực và ủy quyền trong cùng một bước. Tôi nhận được thông tin đăng nhập của người dùng từ máy khách web bằng cách sử dụng xác thực cơ bản để chuyển các thông tin đăng nhập được mã hóa trong tiêu đề HTTP. Vì vậy, phương pháp OnAuthorization của tôi trông như thế này:

public override void OnAuthorization(HttpActionContext actionContext) 
{ 

    string username; 
    string password; 
    if (GetUserNameAndPassword(actionContext, out username, out password)) 
    { 
     if (Membership.ValidateUser(username, password)) 
     { 
      FormsAuthentication.SetAuthCookie(username, false); 
      base.Roles = GetResourceOperationRoles(); 
     } 
     else 
     { 
      FormsAuthentication.SignOut(); 
      base.Roles = ""; 
     } 
    } 
    else 
    { 
     FormsAuthentication.SignOut(); 
     base.Roles = ""; 
    } 
    base.OnAuthorization(actionContext); 
} 

GetUserNameAndPassword lấy các chứng chỉ từ tiêu đề HTTP. Sau đó, tôi sử dụng Membership.ValidateUser để xác thực thông tin xác thực. Tôi có nhà cung cấp dịch vụ thành viên tùy chỉnh và nhà cung cấp vai trò được cắm vào để truy cập cơ sở dữ liệu tùy chỉnh. Nếu người dùng được xác thực thì tôi lấy lại vai trò cho tài nguyên và hoạt động. Từ đó, tôi sử dụng cơ sở OnAuthorization để hoàn tất quy trình cấp phép. Đây là nơi nó bị hỏng.

Nếu người dùng được xác thực, tôi sử dụng các phương thức xác thực biểu mẫu chuẩn để đăng nhập người dùng trong (FormsAuthentication.SetAuthCookie) và nếu họ không đăng xuất được (FormsAuthentication.SignOut). Nhưng vấn đề dường như là cơ sở Lớp OnAuthorization không có quyền truy cập vào Hiệu trưởng được cập nhật sao cho Được xác thực được đặt thành giá trị chính xác. Nó luôn luôn là một bước phía sau. Và tôi đoán là nó đang sử dụng một số giá trị được lưu trong bộ nhớ cache mà không được cập nhật cho đến khi có một chuyến đi khứ hồi tới máy khách web.

Vì vậy, tất cả điều này dẫn đến câu hỏi cụ thể của tôi là, liệu có một cách khác để thiết lập IsAuthenticated với giá trị chính xác cho hiện tại Principal mà không sử dụng cookie? Dường như với tôi rằng cookie không thực sự áp dụng trong trường hợp cụ thể này, nơi tôi phải xác thực mọi lúc.Lý do tôi biết IsAuthenticated không được thiết lập với giá trị đúng là tôi cũng ghi đè phương pháp HandleUnauthorizedRequest này:

protected override void HandleUnauthorizedRequest(HttpActionContext filterContext) 
{ 
    if (((System.Web.HttpContext.Current.User).Identity).IsAuthenticated) 
    { 
     filterContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden); 
    } 
    else 
    { 
     base.HandleUnauthorizedRequest(filterContext); 
    } 
} 

này cho phép tôi để trả lại một mã trạng thái của Tử Cấm cho khách hàng web nếu sự thất bại là do ủy quyền thay vì xác thực và nó có thể phản hồi cho phù hợp.

Vì vậy, cách thích hợp để đặt IsAuthenticated cho hiện tại Nguyên tắc trong trường hợp này là gì?

Trả lời

33

Giải pháp tốt nhất cho kịch bản của tôi dường như bỏ qua cơ sở OnAuthorization hoàn toàn. Kể từ khi tôi phải xác thực mỗi lần cookie và bộ nhớ đệm nguyên tắc không được sử dụng nhiều. Vì vậy, đây là giải pháp tôi đến với:

public override void OnAuthorization(HttpActionContext actionContext) 
{ 
    string username; 
    string password; 

    if (GetUserNameAndPassword(actionContext, out username, out password)) 
    { 
     if (Membership.ValidateUser(username, password)) 
     { 
      if (!isUserAuthorized(username)) 
       actionContext.Response = 
        new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden); 
     } 
     else 
     { 
      actionContext.Response = 
       new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized); 
     } 
    } 
    else 
    { 
     actionContext.Response = 
      new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest); 
    } 
} 

tôi đã phát triển phương pháp riêng của tôi để phê chuẩn các vai trò được gọi là isUserAuthorized và tôi không sử dụng các cơ sở OnAuthorization nữa vì nó kiểm tra hiện hành Nguyên tắc để xem nếu nó được xác thực. IsAuthenticated chỉ cho phép nhận được vì vậy tôi không chắc chắn làm thế nào khác để thiết lập nó, và tôi dường như không cần Nguyên tắc hiện tại. Thử nghiệm điều này và nó hoạt động tốt.

Vẫn quan tâm nếu có ai có giải pháp tốt hơn hoặc có thể thấy bất kỳ vấn đề nào với vấn đề này.

+0

Bạn có thể chia sẻ phần thân phương thức cho GetUserNameAndPassword (actionContext, out username, out password) không? –

+3

@VijayBalkawade - Mã này hiện là một phần của dự án nguồn mở SimpleSecurity. Bạn có thể lấy mã nguồn hoàn chỉnh cho GetUserNameAndPassword tại đây. https://simplesecurity.codeplex.com/SourceControl/latest#SimpleSecurity.Filters/BasicAuthorizeAttribute.cs –

+0

Vì Codeplex sắp ngừng hoạt động, tôi đã [sao chép 'GetUserNameAndPassword' vào một Pastebin] (https://pastebin.com/Yq86aNjL). –

23

Để thêm vào câu trả lời đã được chấp nhận: Kiểm tra mã nguồn hiện tại (aspnetwebstack.codeplex.com) cho System.Web.Http.AuthorizeAttribute, có vẻ như tài liệu đã lỗi thời. Cơ sở OnAuthorization() chỉ cần gọi/kiểm tra riêng tĩnh SkipAuthorization() (chỉ kiểm tra nếu AllowAnonymousAttribute được sử dụng trong ngữ cảnh để bỏ qua phần còn lại của kiểm tra xác thực). Sau đó, nếu không bỏ qua, OnAuthorization() gọi công khai IsAuthorized() và nếu cuộc gọi đó không thực hiện được, thì cuộc gọi đó sẽ được bảo vệ ảo HandleUnauthorizedRequest(). Và đó là tất cả ...

public override void OnAuthorization(HttpActionContext actionContext) 
{ 
    if (actionContext == null) 
    { 
     throw Error.ArgumentNull("actionContext"); 
    } 

    if (SkipAuthorization(actionContext)) 
    { 
     return; 
    } 

    if (!IsAuthorized(actionContext)) 
    { 
     HandleUnauthorizedRequest(actionContext); 
    } 
} 

Nhìn vào bên trong IsAuthorized(), đó là nguyên tắc kiểm tra vai trò và người dùng. Vì vậy, hãy ghi đè IsAuthorized() bằng những gì bạn có ở trên thay vì OnAuthorization() sẽ là cách để thực hiện. Sau đó, một lần nữa, bạn vẫn có thể phải ghi đè hoặc là OnAuthorization() hoặc HandleUnauthorizedRequest() anyway để quyết định thời điểm trả về 401 so với phản hồi 403.

1

Để thêm câu trả lời hoàn toàn chính xác của Kevin, tôi muốn nói rằng tôi có thể sửa đổi một chút để tận dụng đường dẫn .NET framework hiện có cho đối tượng phản hồi để đảm bảo mã hạ lưu trong khung (hoặc người tiêu dùng khác) không bị ảnh hưởng bất lợi bởi một số phong cách riêng kỳ lạ mà không thể dự đoán được.

Cụ thể điều này có nghĩa sử dụng mã này:

actionContext.Response = actionContext.ControllerContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, REQUEST_NOT_AUTHORIZED); 

hơn:

actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized); 

đâu REQUEST_NOT_AUTHORIZED là:

private const string REQUEST_NOT_AUTHORIZED = "Authorization has been denied for this request."; 

Tôi kéo mà string từ SRResources.RequestNotAuthorized định nghĩa trong. Nền tảng NET.

Câu trả lời hay Kevin! Tôi đã triển khai thực hiện cùng một cách vì thực thi OnAuthorization trong lớp cơ sở không có ý nghĩa bởi vì tôi đã xác minh một Tiêu đề HTTP đã được tùy chỉnh cho ứng dụng của chúng tôi và thực sự không muốn kiểm tra Hiệu trưởng vì không có.

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