2011-10-12 18 views
13

Tôi có một trang web đang cố gọi một hành động điều khiển MVC trên một trang web khác. Các trang web này đều được thiết lập như dựa vào tín thác của bên trong AD FS 2.0. Mọi thứ đều xác thực và hoạt động tốt khi mở các trang trong cửa sổ trình duyệt giữa hai trang web. Tuy nhiên, khi cố gọi một hành động điều khiển từ JavaScript bằng phương thức jQuery AJAX, nó luôn thất bại. Dưới đây là đoạn mã về những gì tôi đang cố gắng làm ...Xác thực AD FS 2.0 và AJAX

$.ajax({ 
    url: "relyingPartySite/Controller/Action", 
    data: { foobar }, 
    dataType: "json", 
    type: "POST", 
    async: false, 
    cache: false, 
    success: function (data) { 
    // do something here 
    }, 
    error: function (data, status) { 
    alert(status); 
    } 
}); 

Vấn đề là AD FS sử dụng JavaScript để đăng biểu mẫu html ẩn cho bên phụ thuộc. Khi truy tìm bằng Fiddler, tôi có thể thấy nó vào trang web của AD FS và trả về biểu mẫu html này sẽ đăng và chuyển hướng đến hành động của trình điều khiển đã được xác thực. Vấn đề là hình thức này đang trở lại như là kết quả của yêu cầu ajax và rõ ràng sẽ thất bại với một lỗi phân tích cú pháp vì yêu cầu ajax mong đợi json từ hành động điều khiển. Nó có vẻ như đây sẽ là một kịch bản phổ biến, vì vậy cách thích hợp để giao tiếp với AD FS từ AJAX và xử lý chuyển hướng này là gì?

+1

nếu HTML đang được trả về bởi cuộc gọi ajax, rõ ràng bạn không muốn phân tích cú pháp đó với trình phân tích cú pháp json. thay đổi dataType thành "html" và đăng một ví dụ về html được trả về, vì vậy tôi có thể chỉ cho bạn cách viết trình xử lý sẽ gửi biểu mẫu trả về. – ironchefpython

+0

Vấn đề là tôi muốn lấy lại JSON. AD FS chuyển hướng bằng một biểu mẫu HTML mới mà nó muốn đăng để thực hiện bắt tay mà nó cần. Điều này hoạt động tốt trong một cửa sổ trình duyệt nhưng không có ở đây. Khi bắt tay xảy ra, không có chuyển hướng nào với yêu cầu AJAX và tôi lấy lại JSON. Tôi đã đưa ra một giải pháp cho bây giờ để xử lý các bài đăng trang html trong một IFRAME nhưng nó không phải là lý tưởng. –

+0

Tôi hiểu rằng bạn muốn lấy lại JSON, nhưng bạn sẽ không lấy lại JSON. _However_, nếu bạn muốn có thể xử lý cấu trúc dữ liệu được trả về như thể nó là JSON, hãy đăng một ví dụ về HTML được trả lại và tôi sẽ chỉ cho bạn cách viết trình xử lý sẽ gửi biểu mẫu trả về ** mà không ** sử dụng IFRAME. – ironchefpython

Trả lời

0

Trước hết bạn nói bạn đang cố thực hiện cuộc gọi ajax tới một trang web khác, cuộc gọi của bạn có tuân theo same origin policy trình duyệt web không? Nếu có thì bạn đang mong đợi html như một phản hồi từ máy chủ của bạn, thay đổi datatype của cuộc gọi ajax thành dataType: "html", sau đó chèn biểu mẫu vào DOM của bạn.

0

Có lẽ 2 bài đăng đầu tiên của this serie sẽ giúp bạn. Họ xem xét ADFS và AJAX yêu cầu

Điều tôi nghĩ tôi sẽ cố gắng làm là xem tại sao cookie xác thực không được truyền qua ajax và tìm phương tiện để gửi chúng theo yêu cầu của tôi. Hoặc kết thúc cuộc gọi ajax bằng chức năng xác thực trước bằng cách truy xuất biểu mẫu html, thêm nó vào DOM, gửi nó (hy vọng sẽ đặt cookie tốt), sau đó gửi yêu cầu thích hợp bạn muốn gửi ban đầu

+0

Các cookie được truyền đi muốn bắt tay diễn ra. Vấn đề này là cái bắt tay đang cố gắng xảy ra trên yêu cầu AJAX nếu đây là lần đầu tiên. –

0

Bạn chỉ có thể làm kiểu này datatype

"xml": Treat the response as an XML document that can be processed via jQuery. 

"html": Treat the response as HTML (plain text); included script tags are evaluated. 

"script": Evaluates the response as JavaScript and evaluates it. 

"json": Evaluates the response as JSON and sends a JavaScript Object to the success callback. 

Nếu bạn có thể nhìn thấy trong cáy của bạn cũng được trở về chỉ html sau đó thay đổi kiểu dữ liệu của bạn để html hay nếu đó chỉ là một đoạn mã script sau đó bạn có thể sử dụng kịch bản.

-1

Bạn nên tạo một tập tin anyname như json.php và sau đó đặt kết nối đến trang web relayparty này nên làm việc $.ajax({ url: "json.php", data: { foobar }, dataType: "json", type: "POST", async: false, cache: false, success: function (data) { // do something here }, error: function (data, status) { alert(status); } });

1

Nếu bạn không muốn nhận HTML với các liên kết mà bạn có thể xử lý AuthorizationFailed trên WSFederationAuthenticationModule và đặt RedirectToIdentityProvider thành false chỉ trên các cuộc gọi Ajax.

ví dụ:

FederatedAuthentication.WSFederationAuthenticationModule.AuthorizationFailed += (sender, e) => 
{ 
    if (Context.Request.RequestContext.HttpContext.Request.IsAjaxRequest()) 
    { 
     e.RedirectToIdentityProvider = false; 
    } 
}; 

này với Authorize thuộc tính sẽ đưa bạn trở mã trạng thái 401 và nếu bạn muốn có một cái gì đó khác nhau, sau đó bạn có thể thực hiện riêng Authorize thuộc tính và viết mã đặc biệt trên Ajax Request.

+0

Xử lý 403 trong JS và tải iframe bằng trang html bên trong khu vực an toàn, tất cả chuyển hướng adfs crappy sẽ xuất hiện và bạn sẽ nhận được cookie của mình ... sau đó bạn có thể thử lại cuộc gọi –

+0

Xin lỗi, 401 không 403 –

+0

nên (theo lý thuyết) làm việc tốt trong phương thức 'Application_Start()' của 'Global.asax.cs' - nhưng sẽ không. Tuy nhiên, nó hoạt động tốt trong 'Application_BeginRequest()'. –

3

Bạn có hai tùy chọn. Thông tin thêm here.

Đầu tiên là chia sẻ cookie phiên giữa một ứng dụng mục nhập (dựa trên HTML) và các giải pháp API của bạn. Bạn cấu hình cả hai ứng dụng để sử dụng cùng một cookie WIF. Điều này chỉ hoạt động nếu cả hai ứng dụng nằm trên cùng một miền gốc. Xem bài đăng trên hoặc stackoverflow question này.

Tùy chọn khác là vô hiệu hóa passiveRedirect cho các yêu cầu AJAX (như Gutek's answer). Điều này sẽ trả về mã trạng thái http là 401 mà bạn có thể xử lý trong Javascript. Khi bạn phát hiện 401, bạn tải một trang giả (hoặc hộp thoại "Authenticating" có thể tăng gấp đôi như hộp thoại đăng nhập nếu thông tin đăng nhập cần phải được đưa lại) trong iFrame. Khi iFrame đã hoàn tất, bạn hãy thử lại cuộc gọi. Lần này cookie phiên sẽ có mặt trên cuộc gọi và nó sẽ thành công.

//Requires Jquery 1.9+ 
var webAPIHtmlPage = "http://webapi.somedomain/preauth.html" 

function authenticate() { 
    return $.Deferred(function (d) { 
     //Potentially could make this into a little popup layer 
     //that shows we are authenticating, and allows for re-authentication if needed 
     var iFrame = $("<iframe></iframe>"); 
     iFrame.hide(); 
     iFrame.appendTo("body"); 
     iFrame.attr('src', webAPIHtmlPage); 
     iFrame.load(function() { 
      iFrame.remove(); 
      d.resolve(); 
     }); 
    }); 
}; 

function makeCall() { 
    return $.getJSON(uri) 
       .then(function(data) { 
         return $.Deferred(function(d) { d.resolve(data); }); 
        }, 
        function(error) { 
         if (error.status == 401) { 
          //Authenticating, 
          //TODO:should add a check to prevnet infinite loop 
          return authenticate().then(function() { 
           //Making the call again 
           return makeCall(); 

          }); 
         } else { 
          return $.Deferred(function(d) { 
           d.reject(error); 
          }); 
         } 
       }); 
} 
+0

Giải pháp iFrame chỉ hoạt động cho tôi bằng cách không xóa iframe, nhưng bằng cách lấy nội dung của nó iFrame.load (function() { var content = iFrame.contents(); resolve(); }); – GitteTitter

+0

Tùy chọn thứ hai đang làm việc cho tôi nếu tôi thực hiện GET bằng Ajax. Nhưng nếu tôi thực hiện POST thì trình duyệt sẽ không gửi cookie được tải bởi iFrame cùng với yêu cầu. Bất kỳ ý tưởng tại sao? – NLV

0

Trong dự án mà tôi hiện đang làm việc cùng, chúng tôi gặp vấn đề tương tự với hết hạn mã thông báo SAML trên máy khách và gây ra sự cố với cuộc gọi ajax. Trong trường hợp cụ thể của chúng tôi, chúng tôi cần tất cả các yêu cầu để được enqueud sau khi 401 đầu tiên gặp phải và sau khi xác thực thành công tất cả chúng có thể được gửi lại. Việc xác thực sử dụng giải pháp khung nội tuyến do Adam Mills đề xuất, nhưng cũng đi xa hơn một chút trong trường hợp thông tin người dùng cần được nhập, được thực hiện bằng cách hiển thị hộp thoại thông báo cho người dùng đăng nhập trên chế độ xem bên ngoài (vì ADFS không cho phép hiển thị thông tin đăng nhập trang trong khung nội tuyến ít nhất không phải cấu hình mặc định) trong thời gian chờ yêu cầu đang đợi để hoàn tất nhưng người dùng cần phải đăng nhập từ một trang bên ngoài. Các yêu cầu chờ đợi cũng có thể bị từ chối nếu người dùng chọn Hủy và trong những trường hợp đó, lỗi jquery sẽ được gọi cho mỗi yêu cầu.

Dưới đây là một liên kết đến một ý chính với mã ví dụ:

https://gist.github.com/kaveh82/bb0d8e4a446496a6c05a

Lưu ý mã của tôi là dựa vào cách sử dụng của jquery để xử lý tất cả các yêu cầu ajax. Nếu yêu cầu ajax của bạn đang được xử lý bởi vanilla javascript, các thư viện hoặc khung công tác khác thì bạn có thể tìm thấy một số cảm hứng trong ví dụ này. Việc sử dụng jquery ui chỉ vì hộp thoại và là viết tắt của một phần nhỏ của mã có thể dễ dàng được hoán đổi.