Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Go with SQL Server-Treiber kann keine erfolgreiche Verbindung herstellen, Anmeldung fehlgeschlagen

Ich möchte meine Erfahrungen mit der Ausarbeitung eines einfachen Demo-Datenbankprogramms für die Go-Sprache mit SQL Server Express 2008 teilen. Ich glaube, dass die folgenden Lektionen für alle SQL Server-Versionen ab 2008 gelten.

Mein SQL Server Express wurde zuvor mit dem default installiert Instanz anstelle eines named Beispiel. Es wurde auch installiert, um die Windows-Authentifizierung zu verwenden. Diese beiden Einstellungen waren für andere Entwicklungsarbeiten erforderlich, die ich durchführe. Die andere Arbeit, die ich mache, verwendet SQL Server Express auf demselben PC wie die Anwendung als lokale Datenbank-Engine. Ich hatte erwartet, die Windows-Authentifizierung mit SQL Server in meiner Go-Anwendung verwenden zu können.

Auf der Suche nach einem Treiber und einem kleinen Beispielprogramm zur Verwendung mit einem lokalen SQL Server und Go kam diese Frage bei meiner Suche auf. Ich wollte ein paar zusätzliche Informationen und ein Beispielprogramm hinzufügen, um anderen den Einstieg zu erleichtern und aus meinen Fehlern zu lernen. Ich fand auch diesen Artikel GoLang und MSSQL-Datenbanken:Ein Beispiel hilfreich, besonders nachdem ich genügend Fehler gemacht hatte, um es besser zu verstehen.

Die endgültige Version meines Testprogramms sieht wie folgt aus:

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")
}

Wenn die obige Anwendung zum ersten Mal ausgeführt wird, nachdem die erforderlichen Änderungen an den SQL Server-Einstellungen vorgenommen wurden, wird die folgende Ausgabe generiert. Da die Datenbank beim ersten Start des Programms nicht vorhanden ist, wird die Fehlermeldung angezeigt. Bei späteren Ausführungen ist die Datenbank jedoch vorhanden und es wird keine Fehlermeldung ausgegeben, wenn die Datenbank gelöscht wird.

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

Installieren des SQL Server-Treiberpakets

Als erstes musste ich ein Datenbanktreiberpaket finden, das mit SQL Server funktioniert. Mehrere Stackoverflow-Postings empfahlen github.com/denisenkom/go-mssqldb das wurde also verwendet.

Zur Verwendung von github.com/denisenkom/go-mssqldb Paket musste ich es zuerst mit go get github.com/denisenkom/go-mssqldb aus dem Github-Repository abrufen aus dem Befehls-Shell-Fenster, das durch Ausführen von Git Shell erstellt wurde .

Git Shell ist die Github-Shell, die als Teil der Installation von Git installiert wird. Ich fand heraus, dass ich go get ausführen musste Befehl in der Git Shell damit das go Befehl, um den git zu finden Anwendung und greife auf das Github-Repository zu. Als ich versuchte, go get auszuführen Befehl von einer normalen Befehlsshell sah ich eine Fehlermeldung, die darauf hinweist, dass der git Befehl konnte nicht gefunden werden.

Nach der Installation von go-mssqldb Paket konnte ich meine Beispielanwendung ausführen und es kam immer wieder zu einem Laufzeitfehler von Open() . Die Ausgabe meiner Anwendung war die folgende:

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

TCP-Verbindungen für SQL Server aktivieren

Nach einiger Suche fand ich eine Reihe verschiedener Websites, die alle darauf hinwiesen, dass der Fehler bedeutete, dass meine SQL Server-Instanz nicht für TCP/IP konfiguriert war. Die verschiedenen Beiträge zeigten an, dass ich den Sql Server Configuration Manager verwenden musste um TCP/IP zu aktivieren.

Was ich entdeckt habe, ist, dass es tatsächlich zwei Stellen gibt, an denen TCP/IP aktiviert werden muss. Eine davon war Client Protocols und das war ja schon aktiviert. Das andere war jedoch Protocols for MSSQLSERVER und in diesem wurde TCP/IP deaktiviert. Also habe ich TCP/IP in den Protocols for MSSQLSERVER aktiviert Abschnitt und starteten Sie dann den SQL Server-Dienst mit dem Dienstdienstprogramm der Verwaltung in der Systemsteuerung neu.

Ich hatte jedoch immer noch Probleme mit jeder Art von Abfrage, nachdem ich sql.Open() verwendet hatte . Ich habe eine Anwendungsausgabe gesehen, die eine Variation der folgenden war. Die Fehlermeldung war die gleiche, aber wenn Funktionsaufrufe Fehler hatten, konnten sie sich von einem Lauf zum nächsten ändern. Ich habe versucht, die in sql.Open() angegebene Verbindungszeichenfolge zu ändern ohne Ergebnisse außer verschiedenen Fehlermeldungen.

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

Als ich weiter herumstöberte, fand ich diese Notiz im Github-Repository:

Bekannte Probleme

Die Engine von SQL Server 2008 und 2008 R2 kann Anmeldedatensätze nicht verarbeiten, wenn die SSL-Verschlüsselung nicht deaktiviert ist. Um das Problem mit SQL Server 2008 R2 zu beheben, installieren Sie SQL Server 2008 R2 Service Pack 2. Um das Problem mit SQL Server 2008 zu beheben, installieren Sie Microsoft SQL Server 2008 Service Pack 3 und das kumulative Update-Paket 3 für SQL Server 2008 SP3. Weitere Informationen:http://support.microsoft.com/kb/2653857

Also habe ich die Updates heruntergeladen, die ich eigentlich nie installiert habe. Während ich auf den Download wartete, stöberte ich weiter herum und fand den Ordner, der die eigentliche ausführbare SQL Server-Datei zusammen mit dem Log enthielt Ordner mit einer Reihe von Dateien ERRORLOG , ERRORLOG.1 usw.

SQL Server-Protokolle weisen darauf hin, dass ein SQL Server-Benutzer erforderlich ist

Schauen Sie im ERRORLOG nach file fand ich ein Fehlerprotokoll von SQL Server mit den folgenden Protokollen, die das nächste Puzzleteil lieferten:

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]

Dann stellte ich fest, dass der Go SQL Server-Treiber nicht die Windows-Authentifizierung, sondern stattdessen die SQL Server-Authentifizierung verwendete. Ich hatte versucht, die Windows-Authentifizierung zu verwenden, indem ich eine leere user id= angab das schien aber nicht zu funktionieren. Verwenden Sie also den sqlcmd habe ich einen SQL Server-Benutzer erstellt.

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

Als nächstes habe ich das Microsoft SQL Server Management Studio heruntergeladen und installiert. Dies ist ein anderes Dienstprogramm als der SQL Server-Konfigurations-Manager. Damit habe ich zwei Dinge getan:(1) die SQL Server-Authentifizierung sowie die Windows-Authentifizierung aktiviert und (2) die erforderlichen Berechtigungen für meinen neuen SQL Server-Benutzer gouser bereitgestellt . Dieses Dienstprogramm bot auch eine schöne Benutzeroberfläche zum Durchsuchen des SQL Servers und seiner verschiedenen Datenbanken.

Stellen Sie sicher, dass der von Ihnen erstellte SQL-Benutzer über ausreichende Berechtigungen verfügt, damit er zum Herstellen einer Verbindung mit SQL Server und zum Erstellen einer Datenbank verwendet werden kann.

Einige Überlegungen zur Verwendung der Windows-Authentifizierung

Nach weiteren Recherchen stellte ich fest, dass ich die Windows-Authentifizierung tatsächlich verwenden könnte, jedoch müssen die vollständig qualifizierte Benutzer-ID und das zugehörige Kennwort angegeben werden. Für eine Umgebung, die Active Directory mit dem Domänennamen „AD“ verwendet, wäre die vollqualifizierte Benutzer-ID „AD\userid“ und für den lokalen Host „\userid“. Ich forsche immer noch daran, die Anmeldeinformationen des aktuell angemeldeten Benutzers automatisch verwenden zu können.

Nach noch weiterer Recherche und Hilfestellung bei den Go-Treiberentwicklern sollte die Windows-Authentifizierung mit dem aktuellen sql.Open() möglich sein enthält nicht die Benutzerinformationen, die "user id=;password=;" bedeuten sollte nicht angegeben werden.

Diese Form der automatischen Windows-Authentifizierung für den aktuellen Benutzer ist jedoch nur zulässig, wenn die SQL Server-Instanz Kerberos mit einem gültigen Service Principal Name (SPN) verwendet. Wenn Sie Ihre Instanz von SQL Server neu starten und das folgende Protokoll in Ihrer ERRORLOG-Datei sehen, konnte SQL Server nicht mit Kerberos initialisiert werden.

2016-08-23 18:32:16.77 Server Die SQL Server-Netzwerkschnittstellenbibliothek konnte den Dienstprinzipalnamen (SPN) für den SQL Server-Dienst nicht registrieren. Fehler:0x54b, Status:3. Fehler beim Registrieren eines SPN kann dazu führen, dass die integrierte Authentifizierung auf NTLM statt auf Kerberos zurückgreift. Dies ist eine Informationsmeldung. Weitere Maßnahmen sind nur erforderlich, wenn die Kerberos-Authentifizierung durch Authentifizierungsrichtlinien erforderlich ist.

Siehe auch So stellen Sie sicher, dass Sie die Kerberos-Authentifizierung verwenden, wenn Sie eine Remoteverbindung zu einer Instanz von SQL Server 2005 erstellen, die auch mithilfe von setspn einige zusätzliche Informationen bereitstellt Befehl, um das Problem zu beheben.

Siehe auch Die Bibliothek der SQL-Netzwerkschnittstelle konnte den SPN nicht registrieren.

Über vertrauenswürdige Windows-Authentifizierung (Aktualisiert auf Anfrage von @Richard von @xpt)

Die Windows-Authentifizierung meldet sich bei SQL Server mit Windows-Anmeldeinformationen an, ohne eine Benutzer-ID und ein Kennwort anzugeben. Dies wird als vertrauenswürdige Verbindung für sqlcmd bezeichnet oder ODBC; oder Single-Sign-On für go-mssqldb genannt Go-Treiberpaket.

Von go-mssqldb 's readme in github,

„Benutzer-ID“ – Geben Sie die Benutzer-ID für die SQL Server-Authentifizierung oder die Benutzer-ID für die Windows-Authentifizierung im Format DOMÄNE\Benutzer ein. Wenn unter Windows die Benutzer-ID leer ist oder fehlt, wird Single-Sign-On verwendet.

Also habe ich die folgenden zwei Möglichkeiten mit meinem SQL Server 2008 R2 ausprobiert und beide funktionieren einwandfrei:

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

Beachten Sie, dass die Verwendung von server=localhost fehlschlagen würde, da es wichtig ist, den richtigen Hostnamen zu haben, aus diesem Namen erstellt der Treiber den Kerberos-Dienstprinzipalnamen (SPN) von SQL Server, und dieser Name muss mit dem von SQL Server übereinstimmen. Ich habe bei meinem Test einen korrekten Service Principal Name (SPN) verwendet, damit er funktioniert.