sql >> Base de Datos >  >> RDS >> Sqlserver

Ir con el controlador de SQL Server no se puede conectar correctamente, falla el inicio de sesión

Quiero compartir mi experiencia trabajando en un programa de base de datos de lenguaje Go de demostración simple usando SQL Server Express 2008. Creo que las siguientes lecciones aprendidas se aplicarán a cualquier versión de SQL Server a partir de 2008 y posteriores.

Mi SQL Server Express se instaló previamente con el default instancia en lugar de un named instancia. También se instaló para usar la autenticación de Windows. Ambas configuraciones fueron requeridas por otro trabajo de desarrollo que hago. El otro trabajo que hago usa SQL Server Express en la misma PC que la aplicación como motor de base de datos local. Esperaba poder usar la autenticación de Windows con SQL Server en mi aplicación Go.

Buscando un controlador y un pequeño programa de muestra para usar con un servidor SQL local y Go, surgió esta pregunta en mi búsqueda. Pensé en agregar un poco de información adicional y un programa de muestra para ayudar a otros a comenzar y aprender de mis errores. También encontré este artículo GoLang y bases de datos MSSQL:un ejemplo útil, especialmente después de cometer suficientes errores que lo entendí mejor.

La versión final de mi programa de prueba es la siguiente:

package main

import (
    "fmt"
    "log"
    "database/sql"
     _ "github.com/denisenkom/go-mssqldb"     // the underscore indicates the package is used
)    

func main() {
    fmt.Println("starting app")

    // the user needs to be setup in SQL Server as an SQL Server user.
    // see create login and the create user SQL commands as well as the
    // SQL Server Management Studio documentation to turn on Hybrid Authentication
    // which allows both Windows Authentication and SQL Server Authentication.
    // also need to grant to the user the proper access permissions.
    // also need to enable TCP protocol in SQL Server Configuration Manager.
    //
    // you could also use Windows Authentication if you specify the fully qualified
    // user id which would specify the domain as well as the user id.
    // for instance you could specify "user id=domain\\user;password=userpw;".

    condb, errdb := sql.Open("mssql", "server=localhost;user id=gouser;password=g0us3r;")
    if errdb  != nil {
        fmt.Println("  Error open db:", errdb.Error())
    }

    defer condb.Close()

    errdb = condb.Ping()
    if errdb != nil {
        log.Fatal(errdb)
    }

    // drop the database if it is there so we can recreate it
    // next we will recreate the database, put a table into it,
    // and add a few rows.
    _, errdb = condb.Exec("drop database mydbthing")
    if errdb != nil {
        fmt.Println("  Error Exec db: drop db - ", errdb.Error())
    }

    _, errdb = condb.Exec("create database mydbthing")
    if errdb  != nil {
        fmt.Println("  Error Exec db: create db - ", errdb.Error())
    }

    _, errdb = condb.Exec("use  mydbthing")
    if errdb  != nil {
        fmt.Println("  Error Exec db: using db - ", errdb.Error())
    }

    _, errdb = condb.Exec("create table junky (one int, two int)")
    if errdb  != nil {
        fmt.Println("  Error Exec db: create table - ", errdb.Error())
    }

    _, errdb = condb.Exec("insert into junky (one, two) values (101, 201)")
    if errdb  != nil {
        fmt.Println("  Error Exec db: insert table 1 - ", errdb.Error())
    }
    _, errdb = condb.Exec("insert into junky (one, two) values (102, 202)")
    if errdb  != nil {
        fmt.Println("  Error Exec db: insert table 2 - ", errdb.Error())
    }
    _, errdb = condb.Exec("insert into junky (one, two) values (103, 203)")
    if errdb  != nil {
        fmt.Println("  Error Exec db: insert table 3 - ", errdb.Error())
    }

    // Now that we have our database lets read some records and print them.
    var (
        one  int
        two  int
    )

    // documentation about a simple query and results loop is at URL
    // http://go-database-sql.org/retrieving.html
    // we use Query() and not Exec() as we expect zero or more rows to
    // be returned. only use Query() if rows may be returned.
    fmt.Println ("  Query our table for the three rows we inserted.")
    rows, errdb := condb.Query ("select one, two from junky")
    defer rows.Close()
    for rows.Next() {
        err:= rows.Scan (&one, &two)
        if err != nil {
            fmt.Println("  Error Query db: select - ", err.Error())
        } else {
            fmt.Printf("    - one %d and two %d\n", one, two)
        }
    }
    rows.Close()

    errdb = rows.Err()
    if errdb != nil {
        fmt.Println("  Error Query db: processing rows - ", errdb.Error())
    }

    fmt.Println("ending app")
}

La primera vez que se ejecuta la aplicación anterior una vez que se realizan los cambios necesarios en la configuración de SQL Server, generará el siguiente resultado. Dado que la base de datos no existe la primera vez que se ejecuta el programa, verá impreso el mensaje de error. Sin embargo, las veces posteriores que se ejecute, la base de datos existirá y no aparecerá el mensaje de error cuando la base de datos se elimine.

starting app
  Error Exec db: drop db -  mssql: Cannot drop the database 'mydbthing', because it does not exist or you do not have permission.
  Query our table for the three rows we inserted.
    - one 101 and two 201
    - one 102 and two 202
    - one 103 and two 203
ending app

Instalación del paquete de controladores de SQL Server

Lo primero que tenía que hacer era encontrar un paquete de controlador de base de datos que funcionara con SQL Server. Se recomiendan varias publicaciones de stackoverflow github.com/denisenkom/go-mssqldb así que eso es lo que usé.

Para usar github.com/denisenkom/go-mssqldb paquete Primero tuve que recuperarlo del repositorio de github usando go get github.com/denisenkom/go-mssqldb desde la ventana del shell de comandos creada al ejecutar Git Shell .

Git Shell es el shell de github que se instala como parte de la instalación de Git. Descubrí que tenía que ejecutar go get comando en el Git Shell para que el go comando para encontrar el git aplicación y acceda al repositorio de github. Cuando traté de ejecutar go get comando desde un shell de comando normal, vi un mensaje de error que indicaba que git no se pudo encontrar el comando.

Después de instalar go-mssqldb paquete Pude ejecutar mi aplicación de muestra y seguí encontrando un error de tiempo de ejecución de Open() . El resultado de mi aplicación fue el siguiente:

starting app

Error Exec db: create db -  Unable to open tcp connection with host 'localhost:1433': dial tcp 127.0.0.1:1433: connectex: No connection could be made because the target machine actively refused it.

ending app

Habilitación de conexiones TCP para SQL Server

Después de algunas búsquedas, encontré varios sitios diferentes que indicaban que el error significaba que mi instancia de SQL Server no estaba configurada para TCP/IP. Las diversas publicaciones indicaron que necesitaba usar el Sql Server Configuration Manager para habilitar TCP/IP.

Lo que descubrí es que en realidad hay dos lugares donde se debe habilitar TCP/IP. Uno era Client Protocols y eso de hecho ya estaba habilitado. Sin embargo, el otro era Protocols for MSSQLSERVER y en ese TCP/IP estaba deshabilitado. Así que habilité TCP/IP en los Protocols for MSSQLSERVER y, a continuación, reinicia el servicio de SQL Server utilizando la utilidad Servicio de Herramientas administrativas desde el Panel de control.

Sin embargo, todavía tenía problemas con cualquier tipo de consulta después de usar sql.Open() . Estaba viendo el resultado de la aplicación que era una variación de lo siguiente. El mensaje de error era el mismo, sin embargo, cuando las llamadas a funciones tenían errores, podían cambiar de una ejecución a la siguiente. Intenté cambiar la cadena de conexión especificada en sql.Open() sin más resultados que diferentes mensajes de error.

starting app
  Error Exec db: create db -  driver: bad connection
  Error Exec db: create table -  driver: bad connection
ending app

Buscando más, encontré esta nota en el repositorio de github:

Problemas conocidos

El motor de SQL Server 2008 y 2008 R2 no puede manejar registros de inicio de sesión cuando el cifrado SSL no está deshabilitado. Para solucionar el problema de SQL Server 2008 R2, instale SQL Server 2008 R2 Service Pack 2. Para solucionar el problema de SQL Server 2008, instale Microsoft SQL Server 2008 Service Pack 3 y el paquete de actualización acumulativa 3 para SQL Server 2008 SP3. Más información:http://support.microsoft.com/kb/2653857

Así que descargué las actualizaciones que en realidad nunca instalé. Mientras esperaba la descarga, busqué más y encontré la carpeta que contenía el ejecutable real de SQL Server junto con el Log carpeta que contiene una serie de archivos ERRORLOG , ERRORLOG.1 , etc.

Los registros de SQL Server indican que se requiere un usuario de SQL Server

Buscando en el ERRORLOG Encontré un registro de errores de SQL Server con los siguientes registros que proporcionaron la siguiente pieza del rompecabezas:

2016-08-15 22:56:22.41 Server      SQL Server is now ready for client connections. This is an informational message; no user action is required.
2016-08-15 23:55:47.51 Logon       Error: 18456, Severity: 14, State: 58.
2016-08-15 23:55:47.51 Logon       Login failed for user 'rchamber'. Reason: An attempt to login using SQL authentication failed. Server is configured for Windows authentication only. [CLIENT: 127.0.0.1]
2016-08-15 23:55:47.61 Logon       Error: 18456, Severity: 14, State: 58.
2016-08-15 23:55:47.61 Logon       Login failed for user 'rchamber'. Reason: An attempt to login using SQL authentication failed. Server is configured for Windows authentication only. [CLIENT: ::1]
2016-08-15 23:55:47.62 Logon       Error: 18456, Severity: 14, State: 58.
2016-08-15 23:55:47.62 Logon       Login failed for user 'rchamber'. Reason: An attempt to login using SQL authentication failed. Server is configured for Windows authentication only. [CLIENT: 127.0.0.1]

Luego me di cuenta de que el controlador Go SQL Server no estaba usando la autenticación de Windows, sino que estaba usando la autenticación de SQL Server. Intenté usar la autenticación de Windows especificando un user id= vacío sin embargo, eso no pareció funcionar. Entonces usando el sqlcmd utilidad, creé un usuario de SQL Server.

1> create login gouser with password='g0us3r';
2> go
1> create user gouser for login gouser;
2> go

Luego descargué e instalé Microsoft SQL Server Management Studio. Esta es una utilidad diferente del Administrador de configuración de SQL Server. Usando esto, hice dos cosas:(1) activé la Autenticación de SQL Server así como la Autenticación de Windows y (2) brindé los permisos necesarios para mi nuevo usuario de SQL Server gouser . Esta utilidad también proporcionó una buena interfaz de usuario para explorar SQL Server y sus diversas bases de datos.

Asegúrese de que el usuario de SQL que cree tenga permisos suficientes para que pueda usarse para conectarse a SQL Server y crear una base de datos.

Algunas consideraciones para usar la autenticación de Windows

Después de más investigaciones, descubrí que en realidad podía usar la autenticación de Windows, sin embargo, se debe proporcionar la identificación de usuario completamente calificada y su contraseña. Para un entorno que usa Active Directory con un nombre de dominio de "AD", la identificación de usuario completa sería "AD\userid" y para el host local sería "\userid". Todavía estoy investigando para poder usar automáticamente las credenciales del usuario que ha iniciado sesión actualmente.

Después de investigar más y encontrar ayuda de los desarrolladores del controlador Go, la autenticación de Windows con la versión actual debería ser posible si sql.Open() no incluye la información del usuario que significa "id de usuario =; contraseña =;" no debe especificarse.

Sin embargo, esta forma de autenticación automática de Windows contra el usuario actual solo se permite si la instancia de SQL Server usa Kerberos con un nombre principal de servicio (SPN) válido. Si reinicia su instancia de SQL Server y ve el siguiente registro en su archivo ERRORLOG, SQL Server no pudo inicializarse con Kerberos.

2016-08-23 18:32:16.77 Servidor La biblioteca de interfaz de red de SQL Server no pudo registrar el nombre principal del servicio (SPN) para el servicio de SQL Server. Error:0x54b, estado:3. Si no se registra un SPN, es posible que la autenticación integrada recurra a NTLM en lugar de a Kerberos. Este es un mensaje informativo. Solo se requieren acciones adicionales si las políticas de autenticación requieren la autenticación Kerberos.

Consulte también Cómo asegurarse de que está utilizando la autenticación Kerberos cuando crea una conexión remota a una instancia de SQL Server 2005, que también proporciona información adicional utilizando setspn comando para corregir el problema.

Consulte también La biblioteca de interfaz de red de SQL no pudo registrar el SPN.

Acerca de la autenticación de Windows de confianza (Actualizado según solicitud de @Richard por @xpt)

La autenticación de Windows está iniciando sesión en SQL Server con la credencial de Windows sin especificar una identificación de usuario y una contraseña. Esto se llama conexión de confianza para sqlcmd o ODBC; o llamado Single-Sign-On para go-mssqldb Ir al paquete de controladores.

Desde go-mssqldb 's Léame en github,

"identificación de usuario":ingrese la identificación de usuario de autenticación de SQL Server o la identificación de usuario de autenticación de Windows en el formato DOMINIO\Usuario. En Windows, si el ID de usuario está vacío o falta, se utiliza el inicio de sesión único.

Así que probé las siguientes dos formas con mi SQL Server 2008 R2 y ambas funcionan bien:

condb, errdb := sql.Open("mssql", "server=MyServer;user id=;password=DONTCARE;")
condb, errdb := sql.Open("mssql", "server=MyServer;user id=;password=;")

Tenga en cuenta que el uso de server =localhost fallaría, ya que es importante tener el nombre de host correcto, a partir de ese nombre, el controlador está creando el nombre principal del servicio (SPN) de kerberos de SQL Server y ese nombre debe coincidir con el de SQL Server. Utilicé un nombre principal de servicio (SPN) adecuado con mi prueba para que funcione.