2011-10-12 25 views
13

Tengo un sitio web que intenta llamar a una acción de controlador MVC en otro sitio web. Estos sitios se configuran como fideicomisos de terceros dependientes en AD FS 2.0. Todo se autentica y funciona bien al abrir páginas en la ventana del navegador entre los dos sitios. Sin embargo, al intentar invocar una acción de controlador desde JavaScript utilizando el método jQuery AJAX, siempre falla. Aquí es un fragmento de código de lo que estoy tratando de hacer ...AD FS 2.0 Autenticación y 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); 
    } 
}); 

La cuestión es que AD FS utiliza JavaScript para poder hacer un formulario HTML oculto a la parte que confía. Al rastrear con Fiddler, puedo ver que llegue al sitio de AD FS y devolver este formulario html que debe publicar y redirigir a la acción del controlador autenticada. El problema es que este formulario está volviendo como resultado de la solicitud de AJAX y obviamente va a fallar con un error de analizador ya que la solicitud de AJAX espera que JSON provenga de la acción del controlador. Parece que esto sería un escenario común, entonces, ¿cuál es la forma correcta de comunicarse con AD FS de AJAX y manejar esta redirección?

+1

si HTML es devuelto por la llamada ajax, claramente no desea analizarlo con el analizador json. cambie dataType a "html" y publique un ejemplo del html devuelto, para que pueda mostrarle cómo escribir un controlador que envíe el formulario devuelto. – ironchefpython

+0

El problema es que quiero recuperar JSON. AD FS redirecciona con un nuevo formulario HTML que desea publicar para realizar el intercambio de información que necesita. Esto funciona bien dentro de una ventana del navegador pero no aquí. Una vez que se da el apretón de manos, no hay redirección con la solicitud AJAX y recupero JSON. He encontrado una solución por ahora para manejar la publicación de la página html en un IFRAME, pero no es ideal. –

+0

Entiendo que quieres recuperar JSON, pero no vas a recuperar JSON. _Sin embargo_, si desea poder tratar la estructura de datos devuelta como si fuera _ JSON, publique un ejemplo del HTML devuelto, y le mostraré cómo escribir un controlador que enviará el formulario devuelto ** sin ** usando un IFRAME. – ironchefpython

Respuesta

0

En primer lugar dices que estás tratando de hacer una llamada ajax al otro sitio web, ¿tu llamada se ajusta a same origin policy de los navegadores web? Si lo hace, entonces está esperando html como respuesta de su servidor, cambie datatype de la llamada ajax al dataType: "html", luego inserte el formulario en su DOM.

0

Quizás los 2 primeros mensajes de this serie te ayudarán. Consideran las solicitudes de ADFS y AJAX

Lo que creo que trataría de hacer es ver por qué las cookies de autenticación no se transmiten a través de ajax, y encontrar un medio para enviarlas con mi solicitud. O envuelva la llamada ajax en una función que se autentique previamente recuperando el formulario html, añádalo oculto al DOM, enviándolo (con suerte establecerá las cookies buenas) y luego envíe la solicitud apropiada que desea enviar originalmente

+0

Las cookies se transmiten. Quiere que se produzca el apretón de manos. Este problema es que el apretón de manos está intentando ocurrir en la solicitud de AJAX si es la primera vez. –

0

Usted sólo puede hacer este tipo de tipo de datos

"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. 

Si se puede ver en su violinista que está devolviendo sólo html a continuación, cambiar el tipo de datos a hTML o si que sólo un código de script a continuación, puede utilizar la escritura.

-1

Debe crear un archivo como anyname json.php y luego poner la conexión a la página web relayparty esto debería trabaja $.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

Si no quieren recibir HTML con el enlace que puede manejar AuthorizationFailed en WSFederationAuthenticationModule y configure RedirectToIdentityProvider en false en llamadas Ajax solamente.

por ejemplo:

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

Este con Authorize atributo que volverá código de estado 401 y si usted quiere tener algo diferente, entonces se puede aplicar propio atributo Authorize y escribir código especial en el Ajax de solicitud.

+0

Maneje el 403 en JS y cargue un iframe con una página html dentro del área segura, se producirán todos los redireccionamientos de adfs molestos y obtendrá sus cookies ... puede volver a intentar la llamada –

+0

Lo siento, 401 no 403 –

+0

Esto debería (en teoría) funcionar bien en el método 'Application_Start()' de 'Global.asax.cs' - pero no lo hará. Sin embargo, funciona bien en 'Application_BeginRequest()'. –

3

Tiene dos opciones. Más información here.

El primero es compartir una cookie de sesión entre una aplicación de entrada (una que está basada en HTML) y sus soluciones de API. Configura ambas aplicaciones para usar la misma cookie WIF. Esto solo funciona si ambas aplicaciones están en el mismo dominio raíz. Ver la publicación anterior o este stackoverflow question.

La otra opción es desactivar el pasivoRedirect para solicitudes AJAX (como Gutek's answer). Esto devolverá un código de estado http de 401 que puede manejar en Javascript. Cuando detecta el 401, carga una página ficticia (o un cuadro de diálogo "Autenticación" que podría funcionar como un cuadro de diálogo de inicio de sesión si las credenciales deben otorgarse nuevamente) en un iFrame. Cuando el iFrame se haya completado, intente nuevamente la llamada. Esta vez, la cookie de sesión estará presente en la llamada y debería tener éxito.

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

La solución iFrame solo funcionó para mí al no eliminar el iframe, pero al obtener su contenido iFrame.load (function() { var content = iFrame.contents(); resolve(); }); – GitteTitter

+0

La segunda opción me funciona si hago un GET usando Ajax. Pero si hago un POST, entonces el navegador no envía la cookie cargada por el iFrame junto con la solicitud. ¿Alguna idea de por qué? – NLV

0

En el proyecto que en la actualidad que trabajo, que tenían el mismo problema con vencimiento token de SAML en el clientside y causando problemas con las llamadas ajax. En nuestro caso particular, necesitábamos que todas las solicitudes se hicieran en cola después de que se encontrara el primer 401 y después de una autenticación exitosa, todas podían ser reenviadas. La autenticación utiliza la solución iframe sugerida por Adam Mills, pero también va un poco más lejos en caso de que sea necesario ingresar las credenciales del usuario, lo que se hace mostrando un diálogo que informa al usuario de iniciar sesión en una vista externa (ya que ADFS no permite mostrar el inicio de sesión página en un iframe al menos configuración no predeterminada) durante la cual la solicitud de espera está esperando a ser terminada pero el usuario necesita iniciar sesión desde una página externa. Las solicitudes de espera también se pueden rechazar si el usuario elige Cancelar y en esos casos se solicitará un error de jquery para cada solicitud.

Aquí hay un enlace a una esencia con el código de ejemplo:

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

Nota mi código se basa en el uso de jQuery para el manejo de todas las solicitudes de Ajax. Si su solicitud de ajax está siendo manejada por vainilla javascript, otras bibliotecas o frameworks entonces tal vez pueda encontrar algo de inspiración en este ejemplo. El uso de jquery ui solo se debe al diálogo y representa una pequeña porción del código que fácilmente se puede intercambiar.

Cuestiones relacionadas