11

He creado un servicio WCF REST autohospedado (con algunos adicionales de WCF REST Starter Kit Preview 2). Esta todo trabajando bien.Servicio WCF REST autohospedado y autenticación básica

Ahora estoy tratando de agregar autenticación básica al servicio. Pero estoy golpeando algunos obstáculos bastante grandes en la pila de WCF que me impide hacer esto.

Parece ser que el HttpListener (que servicios WCF con alojamiento propio uso interno en un nivel bajo en la pila WCF) está bloqueando mis intentos de insertar una cabecera WWW-Authenticate en una respuesta autogenerada 401 Unauthorized. ¿Por qué?

Puedo obtener la autenticación funcionando si me olvido de este encabezado WWW-Authenticate (que parece que Microsoft también lo hizo). Pero ese es el problema. Si no devuelvo un encabezado WWW-Authenticate, el navegador web no mostrará su diálogo estándar de "inicio de sesión". El usuario simplemente se enfrentará a una página de error 401 Unauthorized sin forma de iniciar sesión realmente.

Los servicios REST deben ser accesibles tanto para computadoras como para humanos (al menos en el nivel de solicitud GET). Por lo tanto, siento que WCF REST no está cumpliendo con una parte fundamental de REST aquí. ¿Alguien esta de acuerdo conmigo?

¿Alguien ha obtenido la autenticación básica trabajando con un servicio REST WCF autohospedado? Si es así, ¿cómo lo hiciste?

PD: Obviamente, mi intención de utilizar la autenticación básica no segura se basa en la premisa de que también obtendría HTTPS/SSL trabajando para mi servicio también. Pero eso es otro asunto.

PPS: He intentado con WCF REST Contrib (http://wcfrestcontrib.codeplex.com/) y eso tiene exactamente el mismo problema. Parece que esta biblioteca no se ha probado en escenarios autohospedados.

Gracias.

Respuesta

13

Desafortunadamente, he determinado (mediante el análisis del código fuente de referencia WCF y la ayuda de la herramienta Fiddler para la detección de sesión HTTP) que esto es un error en la pila WCF.

Utilizando Fiddler, noté que mi servicio WCF se comportaba de manera diferente a cualquier otro sitio web que utiliza autenticación básica.

Para ser claros, esto es lo que debería ocurrir:

  1. navegador envía GET solicitud sin el conocimiento de que una contraseña es incluso necesario.
  2. El servidor web rechaza la solicitud con un estado 401 Unauthorized e incluye un encabezado WWW-Authenticate que contiene información sobre métodos de autenticación aceptables.
  3. El navegador solicita al usuario que ingrese las credenciales.
  4. El navegador reenvía GET solicitud e incluye el encabezado Authentication apropiado con las credenciales.
  5. Si las credenciales fueron correctas, el servidor web responde con 200 OK y la página web. Si las credenciales fueron incorrectas, el servidor web responde con 401 Unauthorized e incluye el mismo encabezado WWW-Authenticate que el paso 2.

lo que realmente estaba sucediendo con mi servicio WCF fue la siguiente:

  1. navegador envía GET solicitud sin el conocimiento de que una contraseña es incluso necesario.
  2. WCF nota que no hay un encabezado Authentication en la solicitud y rechaza ciegamente la solicitud con un estado 401 Unauthorized e incluye un encabezado WWW-Authenticate. Todo normal hasta ahora.
  3. El navegador solicita credenciales al usuario. Aún normal.
  4. El navegador reenvía GET solicitud que incluye el encabezado Authentication apropiado.
  5. Si las credenciales fueron correctas, el servidor web responde con 200 OK. Todo está bien. Si las credenciales fueron incorrectas, WCF responde con 403 Forbidden y no incluye ningún encabezado adicional como WWW-Authenticate.

Cuando el navegador obtiene el estado 403 Forbidden, no percibe que se trata de un intento de autenticación fallido. Este código de estado está destinado a informar al navegador que la URL a la que intentó acceder no está permitida. No se relaciona con la autenticación de ninguna manera. Esto tiene el efecto secundario terrible de que cuando el usuario escribe su nombre de usuario/contraseña incorrectamente (y el servidor rechaza con 403), entonces el navegador web no reprompe al usuario para que escriba sus credenciales nuevamente. De hecho, el navegador web cree que la autenticación ha tenido éxito y almacena esas credenciales para el resto de la sesión.

Con esto en mente, me pidió aclaraciones:

La RFC 2617 (http://www.faqs.org/rfcs/rfc2617.html#ixzz0eboUfnrl) no se menciona en ninguna parte la utilización del código de estado 403 Forbidden. De hecho, lo que realmente tiene que decir al respecto es la siguiente:

Si el servidor de origen no desea aceptar las credenciales enviadas con una solicitud , es conveniente devolver una respuesta 401 (no autorizado) . La respuesta DEBE incluir un encabezado WWW-Authenticate que contenga al menos un desafío (posiblemente nuevo) aplicable a el recurso solicitado.

WCF no hace ninguno de estos. Tampoco envía correctamente un código de estado 401 Unauthorized. Tampoco incluye un encabezado WWW-Authenticate.

ahora para encontrar el cuerpo del delito dentro del código fuente de WCF:

descubrí que en la clase HttpRequestContext es un método llamado ProcessAuthentication, que contiene lo siguiente (extracto):

if (!authenticationSucceeded) 
{ 
    SendResponseAndClose(HttpStatusCode.Forbidden); 
} 

defiendo Microsoft en muchas cosas, pero esto es indefendible.

Afortunadamente, lo tengo trabajando a un nivel "aceptable". Simplemente significa que si el usuario ingresa accidentalmente su nombre de usuario/contraseña incorrectamente, entonces la única forma de obtener otro intento es cerrar completamente su navegador web y reiniciarlo para volver a intentarlo.Todo porque WCF es no que responde al intento de autenticación fallido con un encabezado 401 Unauthorized y WWW-Authenticate según la especificación.

+0

Es hora de cambiar a un marco de http real como openrasta :) – SerialSeb

+0

@serialseb No tuve el coraje de decirle que renuncié a tratar de resolver problemas de autenticación en WCF y fui directamente a HttpListener directamente. –

+0

Saludos chicos :) Darrel, vi tus comentarios en otra discusión y créanme lo he considerado como una opción. Afortunadamente, aún no hemos invertido demasiado en WCF REST. Entonces podemos seguir adelante y abandonarlo. – nbevans

6

He encontrado la solución basada en este link y this.

La primera es para anular el método Validar en una clase llamada CustomUserNameValidator wich hereda de System.IdentityModel.Selectors.UserNamePasswordValidator:

Imports System.IdentityModel.Selectors 
Imports System.ServiceModel 
Imports System.Net 

Public Class CustomUserNameValidator 
    Inherits UserNamePasswordValidator 

Public Overrides Sub Validate(userName As String, password As String) 
    If Nothing = userName OrElse Nothing = password Then 
     Throw New ArgumentNullException() 
    End If 

    If Not (userName = "user" AndAlso password = "password") Then 
     Dim exep As New AddressAccessDeniedException("Incorrect user or password") 
     exep.Data("HttpStatusCode") = HttpStatusCode.Unauthorized 
     Throw exep 
    End If 
End Sub 
End Class 

El truco era cambiar el atributo de la excepción "HttpStatusCode "para HttpStatusCode.Unauthorized

el segundo es crear la serviceBehavior en el App.config como sigue:

<behaviors> 
    <endpointBehaviors> 
    <behavior name="HttpEnableBehavior"> 
     <webHttp /> 
    </behavior> 
    </endpointBehaviors> 
    <serviceBehaviors> 
    <behavior name="SimpleServiceBehavior"> 
     <serviceMetadata httpGetEnabled="true"/> 
     <serviceDebug includeExceptionDetailInFaults="false"/> 
     <serviceCredentials> 
     <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Service.CustomUserNameValidator, Service"/> 
     </serviceCredentials> 
    </behavior> 
    </serviceBehaviors> 
</behaviors> 

Por último tenemos que especificar la configuración para el webHttpBinding y aplicarlo al punto final:

<bindings> 
    <webHttpBinding> 
    <binding name="basicAuthBinding"> 
     <security mode="TransportCredentialOnly"> 
     <transport clientCredentialType="Basic" realm=""/> 
     </security> 
    </binding> 
    </webHttpBinding> 
</bindings> 

<service behaviorConfiguration="SimpleServiceBehavior" name="Service.webService"> 
    <host> 
     <baseAddresses> 
     <add baseAddress="http://localhost:8000/webService" /> 
     </baseAddresses> 
    </host> 
    <endpoint address="http://localhost:8000/webService/restful" binding="webHttpBinding" bindingConfiguration="basicAuthBinding" contract="Service.IwebService" behaviorConfiguration="HttpEnableBehavior"/> 
</service> 
1

Sí se puede proporcionar la autenticación básica para servicios WCF basado en REST. Sin embargo, hay varios pasos que debe seguir para tener una solución completa y segura y hasta ahora la mayoría de las respuestas son fragmentos de todas las piezas necesarias.

  1. Configure su servicio autohospedado para tener un certificado SSL vinculado al puerto en el que está alojando su servicio WCF. Esto es muy diferente a la aplicación de un certificado SSL al usar un hosting administrado a través de algo como IIS. Debe aplicar el certificado SSL utilizando una utilidad de línea de comandos. Usted NO desea desea utilizar la Autenticación básica en un servicio REST sin utilizando SSL porque las credenciales en el encabezado no son seguras. Aquí hay (2) publicaciones detalladas que escribí en exactamente cómo hacerlo. Su pregunta es demasiado grande como para tener todos los detalles en un mensaje del foro, así que es por eso que estoy proporcionando los vínculos con amplios detalles y instrucciones paso a paso:

    Applying and Using a SSL Certificate With A Self-Hosted WCF Service

    Creating a WCF RESTful Service And Secure It Using HTTPS Over SSL

  2. Configure su servicio para usar autenticación básica. Esta es una solución de varias partes también. Primero está configurando su servicio para usar autenticación básica. El segundo es crear un 'customUserNamePasswordValidatorType' e inspeccionar las credenciales a autenticar el cliente. Veo que la última publicación eludió esto, sin embargo, hizo no usar HTTPS y es solo 1 parte muy pequeña de la solución; tenga cuidado con la guía que no incluye que proporcionó una solución de extremo a extremo que incluye la configuración y de seguridad. El último paso es mirar el contexto de seguridad para proporcionar la autorización en el nivel del método si es necesario. La siguiente publicación que escribí lo guía paso a paso sobre cómo configurar, autenticar, y autorizar a sus clientes.

    RESTful Services: Authenticating Clients Using Basic Authentication

Esta es la solución de extremo a extremo necesario para utilizar la autenticación básica con servicios WCF con alojamiento propio.

Cuestiones relacionadas