Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Vai con il driver di SQL Server non è in grado di connettersi correttamente, accesso non riuscito

Voglio condividere la mia esperienza con l'elaborazione di un semplice programma di database in lingua Go dimostrativo utilizzando SQL Server Express 2008. Credo che le seguenti lezioni apprese si applicheranno a qualsiasi versione di SQL Server dal 2008 in poi.

Il mio SQL Server Express è stato precedentemente installato con il default istanza anziché un named esempio. È stato anche installato per utilizzare l'autenticazione di Windows. Entrambe queste impostazioni sono state richieste da altri lavori di sviluppo che svolgo. L'altro lavoro che faccio utilizza SQL Server Express sullo stesso PC dell'applicazione come motore di database locale. Mi aspettavo di poter utilizzare l'autenticazione di Windows con SQL Server nella mia applicazione Go.

Alla ricerca di un driver e di un piccolo programma di esempio da utilizzare con un SQL Server locale e Go, questa domanda è emersa nella mia ricerca. Ho pensato di aggiungere un po' di informazioni aggiuntive e un programma di esempio per aiutare gli altri a iniziare e imparare dai miei errori. Ho anche trovato questo articolo GoLang e database MSSQL:un esempio utile soprattutto dopo aver commesso errori sufficienti per capirlo meglio.

La versione finale del mio programma di test è la seguente:

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 prima volta che l'applicazione precedente viene eseguita dopo aver apportato le modifiche necessarie alle impostazioni di SQL Server, genererà l'output seguente. Poiché il database non esiste la prima volta che viene eseguito il programma, verrà visualizzato il messaggio di errore stampato. Tuttavia, le volte successive che viene eseguito, il database esisterà e il messaggio di errore quando il database viene eliminato non verrà emesso.

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

Installazione del pacchetto del driver di SQL Server

La prima cosa che dovevo fare era trovare un pacchetto di driver di database che funzionasse con SQL Server. Diversi post di stackoverflow consigliati github.com/denisenkom/go-mssqldb quindi è quello che ha usato.

Per utilizzare github.com/denisenkom/go-mssqldb pacchetto Ho dovuto prima recuperarlo dal repository github usando go get github.com/denisenkom/go-mssqldb dalla finestra della shell dei comandi creata eseguendo Git Shell .

Git Shell è la shell github che viene installata come parte dell'installazione di Git. Ho scoperto che dovevo eseguire go get comando nella Git Shell in ordine per il go comando per trovare il git applicazione e accedi al repository github. Quando ho provato a eseguire go get comando da una normale shell dei comandi Ho visto un messaggio di errore che indicava che git Impossibile trovare il comando.

Dopo aver installato go-mssqldb pacchetto Sono stato in grado di eseguire la mia applicazione di esempio e ho continuato a riscontrare un errore di runtime da Open() . L'output della mia applicazione era il seguente:

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

Abilitazione delle connessioni TCP per SQL Server

Dopo alcune ricerche ho trovato diversi siti che indicavano tutti che l'errore significava che la mia istanza di SQL Server non era configurata per TCP/IP. I vari messaggi indicavano che dovevo usare Sql Server Configuration Manager per abilitare TCP/IP.

Quello che ho scoperto è che in realtà ci sono due posti in cui è necessario abilitare TCP/IP. Uno era Client Protocols e in effetti era già abilitato. Tuttavia l'altro era Protocols for MSSQLSERVER e in quello TCP/IP era disabilitato. Quindi ho abilitato TCP/IP in Protocols for MSSQLSERVER sezione, quindi riavviato il servizio SQL Server utilizzando l'utilità di servizio degli Strumenti di amministrazione dal Pannello di controllo.

Tuttavia avevo ancora problemi con qualsiasi tipo di query dopo aver usato sql.Open() . Stavo vedendo l'output dell'applicazione che era una variazione di quanto segue. Il messaggio di errore era lo stesso, tuttavia quando le chiamate di funzione presentavano errori potevano cambiare da un'esecuzione all'altra. Ho provato a modificare la stringa di connessione specificata in sql.Open() senza risultati diversi da diversi messaggi di errore.

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

Sbirciando ulteriormente ho trovato questa nota nel repository github:

Problemi noti

Il motore di SQL Server 2008 e 2008 R2 non è in grado di gestire i record di accesso quando la crittografia SSL non è disabilitata. Per risolvere il problema di SQL Server 2008 R2, installare SQL Server 2008 R2 Service Pack 2. Per risolvere il problema di SQL Server 2008, installare Microsoft SQL Server 2008 Service Pack 3 e il pacchetto di aggiornamento cumulativo 3 per SQL Server 2008 SP3. Ulteriori informazioni:http://support.microsoft.com/kb/2653857

Quindi ho scaricato gli aggiornamenti che non ho mai effettivamente installato. Durante l'attesa del download, ho dato un'occhiata di più e ho trovato la cartella contenente l'effettivo eseguibile di SQL Server insieme al Log cartella contenente una serie di file ERRORLOG , ERRORLOG.1 , ecc.

I registri di SQL Server indicano la richiesta dell'utente di SQL Server

Guardando nel ERRORLOG file Ho trovato un registro degli errori di SQL Server con i seguenti registri che hanno fornito il prossimo pezzo del puzzle:

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]

Mi sono quindi reso conto che il driver Go SQL Server non utilizzava l'autenticazione di Windows ma utilizzava invece l'autenticazione di SQL Server. Ho provato a utilizzare l'autenticazione di Windows specificando un user id= vuoto tuttavia che non sembrava funzionare. Quindi usando sqlcmd utility, ho creato un utente SQL Server.

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

Successivamente ho scaricato e installato Microsoft SQL Server Management Studio. Si tratta di un'utilità diversa da Gestione configurazione SQL Server. Usando questo ho fatto due cose:(1) ho attivato l'autenticazione di SQL Server e l'autenticazione di Windows e (2) ho fornito le autorizzazioni necessarie per il mio nuovo utente di SQL Server gouser . Questa utility ha anche fornito una bella interfaccia utente per esplorare SQL Server e i suoi vari database.

Assicurati che l'utente SQL che crei disponga di autorizzazioni sufficienti in modo che possa essere utilizzato per connettersi a SQL Server e creare un database.

Alcune considerazioni sull'utilizzo dell'autenticazione di Windows

Dopo ulteriori ricerche ho scoperto che potevo effettivamente utilizzare l'autenticazione di Windows, tuttavia è necessario fornire l'ID utente completamente qualificato e la relativa password. Per un ambiente che utilizza Active Directory con un nome di dominio "AD", l'ID utente completo sarebbe "AD\userid" e per l'host locale sarebbe "\userid". Sto ancora cercando di poter utilizzare automaticamente le credenziali dell'utente attualmente connesso.

Dopo ulteriori ricerche e assistenza da parte degli sviluppatori del driver Go, l'autenticazione di Windows con l'attuale dovrebbe essere possibile se sql.Open() non include le informazioni sull'utente che significano "id utente=;password=;" non deve essere specificato.

Tuttavia, questa forma di autenticazione automatica di Windows per l'utente corrente è consentita solo se l'istanza di SQL Server utilizza Kerberos con un nome dell'entità servizio (SPN) valido. Se esegui un riavvio sull'istanza di SQL Server e vedi il registro seguente nel file ERRORLOG, SQL Server non è stato in grado di inizializzare con Kerberos.

2016-08-23 18:32:16.77 Server La libreria dell'interfaccia di rete di SQL Server non ha potuto registrare il nome dell'entità servizio (SPN) per il servizio SQL Server. Errore:0x54b, stato:3. La mancata registrazione di un SPN può causare il fallback dell'autenticazione integrata su NTLM anziché su Kerberos. Questo è un messaggio informativo. Ulteriori azioni sono richieste solo se l'autenticazione Kerberos è richiesta dai criteri di autenticazione.

Vedere anche Come assicurarsi di utilizzare l'autenticazione Kerberos quando si crea una connessione remota a un'istanza di SQL Server 2005 che fornisce anche alcune informazioni aggiuntive utilizzando setspn comando per correggere il problema.

Vedi anche La libreria dell'interfaccia di rete SQL non è stata in grado di registrare SPN.

Informazioni sull'autenticazione Windows affidabile (Aggiornato secondo richiesta da @Richard da @xpt)

L'autenticazione di Windows sta accedendo a SQL Server con credenziali di Windows senza specificare un ID utente e una password. Questa è chiamata connessione affidabile per sqlcmd o ODBC; o chiamato Single-Sign-On per go-mssqldb Vai pacchetto driver.

Da go-mssqldb 's readme in github,

"ID utente":immettere l'ID utente di autenticazione di SQL Server o l'ID utente di WindowsAuthentication nel formato DOMINIO\Utente. Su Windows, se l'ID utente è vuoto o manca, viene utilizzato il Single Sign-On.

Quindi ho provato i seguenti due modi con il mio SQL Server 2008 R2 ed entrambi funzionano bene:

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

Si noti che l'utilizzo di server=localhost non riuscirebbe, poiché è importante disporre del nome host corretto, da quel nome il driver sta creando il nome dell'entità servizio (SPN) Kerberos di SQL Server e quel nome deve corrispondere a quello di SQL Server. Ho utilizzato un nome dell'entità servizio (SPN) corretto con il mio test, quindi funziona.