Acceso a archivos en el servidor mediante la especificación de credenciales

Nuestra empresa tiene un servidor de documentos de puntos compartidos donde la UNC se parece a esto: \\theserver.ourdomain.com\rootdirectory

Actualmente, esta unidad está asignada a Z:\ en mi computadora local. Para acceder a Z:\, debe especificar (cada vez que inicie sesión) las credenciales (en nuestro caso, es nuestro nombre de usuario y contraseña con los que iniciamos sesión) para acceder a las carpetas y archivos en el directorio raíz.

Estoy en una situación en la que necesito copiar archivos en el servidor de puntos compartidos. Quiero poder copiar archivos en el servidor sin usar la unidad de red asignada (no tengo que especificar Z:\ en la ruta). ¿Cómo puedo proporcionar credenciales para poder realizar funciones básicas de IO como GetDirectories(), GetFiles(), IO.File.Copy(), etc.?

He investigado las siguientes cosas, pero no he podido hacer que funcionen:

  1. Logonuser Llamada API especificando el nombre de usuario y la contraseña de texto sin formato, luego tomando el token de esa llamada y suplantando a ese usuario usando una nueva instancia de la clase WindowsIdentity. Pude obtener el token, pero la suplantación no pareció funcionar. Seguía recibiendo errores de acceso denegado.
  2. CredUIPromptForCredentials/CredUIPromptForWindowsCredentials Llamadas API, pero me doy cuenta de que estas son solo para una interfaz de usuario elegante de Windows en la que puede ingresar sus credenciales y en realidad no hacer nada.

    <DllImport("advapi32.dll", SetLastError:=True)> _
    Private Shared Function LogonUser(lpszUsername As String, lpszDomain As String, _
                                              lpszPassword As String, dwLogonType As Integer, _
                                              dwLogonProvider As Integer, ByRef phToken As IntPtr) As Boolean
    End Function
    
    <DllImport("kernel32.dll", CharSet:=CharSet.Auto)> _
    Private Shared Function CloseHandle(handle As IntPtr) As Boolean
    End Function
    
    '// logon types
    Public Const LOGON32_LOGON_NETWORK As Integer = 3
    Public Const LOGON32_LOGON_NEW_CREDENTIALS As Integer = 9
    
    '// logon providers
    Public Const LOGON32_PROVIDER_WINNT50 As Integer = 3
    Public Const LOGON32_PROVIDER_WINNT40 As Integer = 2
    Public Const LOGON32_PROVIDER_WINNT35 As Integer = 1
    Public Const LOGON32_PROVIDER_DEFAULT As Integer = 0
    
    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim token = IntPtr.Zero
        Dim success = LogonUser("username", "domain", "password", _
                                LOGON32_LOGON_NEW_CREDENTIALS, _
                                LOGON32_PROVIDER_DEFAULT, token)
    
        If Not success Then
            Me.RaiseLastWin32Error()
        End If
    
        Using identity = New WindowsIdentity(token)
            Using impersonated = identity.Impersonate()
                Try
                    Dim info = New DirectoryInfo("\\theserver.ourdomain.com\rootdirectory\")
                    Dim files = info.GetDirectories()
                Catch ex As Exception
                Finally
                    impersonated.Undo()
                End Try
                If Not CloseHandle(token) Then
                    Me.RaiseLastWin32Error()
                End If
            End Using
        End Using
    
    End Sub
    
    Private Sub RaiseLastWin32Error()
        Dim hr = Marshal.GetLastWin32Error()
        Dim ex = Marshal.GetExceptionForHR(hr)
    
        If ex IsNot Nothing Then
            Throw ex
        End If
        Throw New SystemException(String.Format("Call resulted in error code {0}", hr))
    End Sub
    

preguntado el 12 de junio de 12 a las 21:06

Un recurso adicional de MSDN: msdn.microsoft.com/en-us/library/chf6fbt4.aspx. Al darse cuenta de que ya ha intentado esto (# 1 en su publicación), es posible que deba mostrar algún código para diagnosticar por qué está obteniendo excepciones. -

Código agregado a la publicación anterior:

El código en el otros post y el ejemplo de MSDN son ligeramente diferentes y más complicados de lo que ha intentado. Dales una oportunidad a ambos e informa. -

Los probé y no funcionan. Todavía obtengo un error de acceso denegado. Piensa que uno de nosotros no está entendiendo algo. No debería tener que suplantarme para acceder al servidor, ya estoy conectado como el usuario que quiero acceder a los archivos. Debe haber algo que me estoy perdiendo. -

2 Respuestas

Esta no es una respuesta directa a su pregunta, ya que es un enfoque muy diferente. Si no funciona para su situación, disculpe las molestias, pero ¿ha considerado usar los servicios web de SharePoint para cargar los archivos y recuperar información?

Sugiero este enfoque por varias razones:

  1. El problema que está experimentando puede deberse a que SharePoint implementa WebDav, que podría no ser 100 % compatible con System.IO. No soy un experto en las entrañas aquí, no sé con seguridad la compatibilidad, pero parece plausible.
  2. La ubicación UNC que tiene se podría convertir fácilmente en una URL que requiere el servicio web.
  3. Puede configurar las credenciales directamente en el proxy y puede que le resulte más fácil. (aunque hacemos estas llamadas desde otro servidor web, por lo que las credenciales del grupo de aplicaciones en el ejemplo son lo suficientemente buenas para nosotros)

Aquí hay un código desinfectado y simplificado por si acaso:

// location takes the form http://server.name.com/site/library/folder/document.ext

public string UploadDocument(string location, byte[] fileContents)
{
    var result = String.empty;
    var destination = new string[1];
    destination[0] = location;
    var fileName = Path.GetFileName(location);
    var fieldInfo = new FieldInformation[0];
    CopyResult[] copyResults;

    _copyService.Url = "http://server.name.com/_vti_bin/Copy.asmx";
    _copyService.Credentials = CredentialCache.DefaultCredentials;
    _copyService.CopyIntoItems(fileName, destination, fieldInfo, fileContents, out copyResults);

    var errorCode = copyResults[0].ErrorCode;
    if (errorCode != CopyErrorCode.Success)
    {
        if (errorCode == CopyErrorCode.DestinationCheckedOut)
            result = "File is currently checked out. Please try again later.";
        else
            result = "Error uploading content.";
    }

    return result;
}

_copyService es una dependencia que inyectamos donde la implementación en tiempo de ejecución es el proxy generado por las herramientas de Visual Studio desde el servicio web de SharePoint Copy.asmx.

También puede obtener contenido de carpetas y metadatos de documentos mediante el servicio web Lists.asmx. Las mayores desventajas de este enfoque son que consultar la información requiere cierto conocimiento de CAML y procesar los resultados no es tan fácil. Pero los servicios están razonablemente documentados en MSDN y todas las operaciones funcionan en nuestra aplicación.

Respondido el 12 de junio de 12 a las 22:06

Gracias por la respuesta. Puedo acceder a los archivos y directorios a través del código si yo (a través del Explorador) proporciono mi contraseña y nombre de usuario a la unidad de red asignada según lo solicita. También me preocupan los problemas de rendimiento si sigo la ruta que sugieres. - test

Interesante. Tendré curiosidad por encontrar la solución yo mismo entonces, porque veo de dónde vienes en el ángulo de rendimiento. - Arrojar

Bueno, pude resolver esto con la ayuda de la API WNetAddConnection2. Esta API también se usa para mapear unidades de red; sin embargo, puede llamar a este método sin especificar una letra de unidad para que solo agregue la conexión.

Digamos, por ejemplo, que tenía la unidad X: asignada a \\servidor\compartir. Digamos también que requiere nombre de usuario y contraseña para acceder a los archivos en el servidor. Cuando reinicie Windows 7, probablemente perderá esa conexión (recibirá una notificación que indica que Windows no pudo volver a conectar algunas de las unidades de red). Si tiene una aplicación que requiere acceso a los archivos de ese servidor e intenta acceder a ella sin proporcionar sus credenciales, obtendrá excepciones de acceso denegado. Si realiza una llamada exitosa a WNetAddConnection2, no solo reparará su unidad de red no asignada, sino que también podrá acceder a los archivos/directorios a través del espacio de nombres System.IO.

Usamos Sharepoint y esto funcionó para mí. Gracias a los otros chicos por responder también.

Respondido el 13 de junio de 12 a las 21:06

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.