Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Le pilote Go with SQL Server ne parvient pas à se connecter correctement, la connexion échoue

Je souhaite partager mon expérience en travaillant sur un simple programme de démonstration de base de données en langage Go à l'aide de SQL Server Express 2008. Je pense que les leçons apprises suivantes s'appliqueront à toutes les versions de SQL Server à partir de 2008.

Mon SQL Server Express était précédemment installé avec le default instance plutôt qu'un named exemple. Il a également été installé pour utiliser l'authentification Windows. Ces deux paramètres étaient requis par d'autres travaux de développement que je fais. L'autre travail que j'effectue utilise SQL Server Express sur le même PC que l'application en tant que moteur de base de données local. Je m'attendais à pouvoir utiliser l'authentification Windows avec SQL Server dans mon application Go.

À la recherche d'un pilote et d'un petit exemple de programme à utiliser avec un serveur SQL local et Go, cette question s'est posée dans ma recherche. J'ai pensé ajouter quelques informations supplémentaires et un exemple de programme pour aider les autres à démarrer et à apprendre de mes erreurs. J'ai également trouvé cet article Bases de données GoLang et MSSQL :un exemple utile, surtout après avoir fait suffisamment d'erreurs pour mieux le comprendre.

La version finale de mon programme de test est la suivante :

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 première fois que l'application ci-dessus est exécutée une fois les modifications nécessaires apportées aux paramètres SQL Server, elle génère la sortie suivante. Étant donné que la base de données n'existe pas la première fois que le programme est exécuté, vous verrez le message d'erreur imprimé. Cependant, les fois suivantes, la base de données existera et le message d'erreur lorsque la base de données est supprimée n'existera pas.

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

Installation du package du pilote SQL Server

La première chose que je devais faire était de trouver un package de pilote de base de données qui fonctionnerait avec SQL Server. Plusieurs publications de stackoverflow recommandées github.com/denisenkom/go-mssqldb c'est donc ce qui a été utilisé.

Pour utiliser le github.com/denisenkom/go-mssqldb package, je devais d'abord le récupérer à partir du référentiel github en utilisant go get github.com/denisenkom/go-mssqldb depuis la fenêtre du shell de commande créée en exécutant Git Shell .

Git Shell est le shell github qui est installé dans le cadre de l'installation de Git. J'ai trouvé que je devais exécuter le go get commande dans le Git Shell dans l'ordre pour le go commande pour trouver le git application et accédez au référentiel github. Quand j'ai essayé d'exécuter le go get commande à partir d'un shell de commande normal, j'ai vu un message d'erreur indiquant que le git la commande est introuvable.

Après avoir installé go-mssqldb J'ai pu exécuter mon exemple d'application et j'ai continué à rencontrer une erreur d'exécution à partir de Open() . Le résultat de mon application était le suivant :

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

Activation des connexions TCP pour SQL Server

Après quelques recherches, j'ai trouvé un certain nombre de sites différents qui indiquaient tous que l'erreur signifiait que mon instance SQL Server n'était pas configurée pour TCP/IP. Les différentes publications indiquaient que je devais utiliser le Sql Server Configuration Manager pour activer TCP/IP.

Ce que j'ai découvert, c'est qu'il y a en fait deux endroits où TCP/IP doit être activé. L'un était Client Protocols et qui était en effet déjà activé. Cependant, l'autre était Protocols for MSSQLSERVER et dans celui-là TCP/IP a été désactivé. J'ai donc activé TCP/IP dans les Protocols for MSSQLSERVER section, puis redémarré le service SQL Server à l'aide de l'utilitaire Service des Outils d'administration du Panneau de configuration.

Cependant, j'avais toujours des problèmes avec tout type de requête après avoir utilisé sql.Open() . Je voyais une sortie d'application qui était une variation de ce qui suit. Le message d'erreur était le même, mais lorsque les appels de fonction avaient des erreurs, cela pouvait changer d'une exécution à l'autre. J'ai essayé de changer la chaîne de connexion spécifiée dans le sql.Open() sans résultat autre que différents messages d'erreur.

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

En fouillant plus loin, j'ai trouvé cette note dans le dépôt github :

Problèmes connus

Le moteur SQL Server 2008 et 2008 R2 ne peut pas gérer les enregistrements de connexion lorsque le chiffrement SSL n'est pas désactivé. Pour résoudre le problème de SQL Server 2008 R2, installez SQL Server 2008 R2 Service Pack 2. Pour résoudre le problème de SQL Server 2008, installez Microsoft SQL Server 2008 Service Pack 3 et le package de mise à jour cumulative 3 pour SQL Server 2008 SP3. Plus d'informations :http://support.microsoft.com/kb/2653857

J'ai donc téléchargé les mises à jour que je n'ai jamais installées. En attendant le téléchargement, j'ai fouillé davantage et j'ai trouvé le dossier contenant l'exécutable réel de SQL Server avec le Log dossier contenant une série de fichiers ERRORLOG , ERRORLOG.1 , etc.

Les journaux SQL Server indiquent que l'utilisateur SQL Server est requis

Recherche dans le ERRORLOG J'ai trouvé un journal d'erreurs de SQL Server avec les journaux suivants qui ont fourni la prochaine pièce du 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]

J'ai alors réalisé que le pilote Go SQL Server n'utilisait pas l'authentification Windows mais utilisait à la place l'authentification SQL Server. J'avais essayé d'utiliser l'authentification Windows en spécifiant un user id= vide cependant cela n'a pas semblé fonctionner. Donc, en utilisant le sqlcmd utilitaire, j'ai créé un utilisateur SQL Server.

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

Ensuite, j'ai téléchargé et installé Microsoft SQL Server Management Studio. Il s'agit d'un utilitaire différent du gestionnaire de configuration SQL Server. En utilisant cela, j'ai fait deux choses :(1) activé l'authentification SQL Server ainsi que l'authentification Windows et (2) fourni les autorisations nécessaires pour mon nouvel utilisateur SQL Server gouser . Cet utilitaire a également fourni une interface utilisateur agréable pour parcourir le serveur SQL et ses différentes bases de données.

Assurez-vous que l'utilisateur SQL que vous créez dispose des autorisations suffisantes pour pouvoir l'utiliser pour se connecter à SQL Server et créer une base de données.

Quelques considérations pour l'utilisation de l'authentification Windows

Après des recherches plus approfondies, j'ai découvert que je pouvais réellement utiliser l'authentification Windows, mais l'identifiant d'utilisateur complet et son mot de passe doivent être fournis. Pour un environnement utilisant Active Directory avec un nom de domaine "AD", l'ID utilisateur complet serait "AD\userid" et pour l'hôte local serait "\userid". Je cherche toujours à pouvoir utiliser automatiquement les informations d'identification de l'utilisateur actuellement connecté.

Après d'autres recherches et l'assistance des développeurs de pilotes Go, l'authentification Windows avec le courant devrait être possible si le sql.Open() n'inclut pas les informations de l'utilisateur signifiant "ID utilisateur=;mot de passe=;" ne doit pas être spécifié.

Cependant, cette forme d'authentification Windows automatique par rapport à l'utilisateur actuel n'est autorisée que si l'instance SQL Server utilise Kerberos avec un nom principal de service (SPN) valide. Si vous effectuez un redémarrage sur votre instance de SQL Server et que vous voyez le journal suivant dans votre fichier ERRORLOG, SQL Server n'a pas pu s'initialiser avec Kerberos.

2016-08-23 18:32:16.77 Serveur La bibliothèque de l'interface réseau SQL Server n'a pas pu enregistrer le nom principal du service (SPN) pour le service SQL Server. Erreur :0x54b, état :3. L'échec de l'enregistrement d'un SPN peut entraîner le retour de l'authentification intégrée à NTLM au lieu de Kerberos. Il s'agit d'un message d'information. Une action supplémentaire n'est requise que si l'authentification Kerberos est requise par les stratégies d'authentification.

Voir aussi Comment s'assurer que vous utilisez l'authentification Kerberos lorsque vous créez une connexion à distance à une instance de SQL Server 2005 qui fournit également des informations supplémentaires à l'aide de setspn commande pour corriger le problème.

Voir aussi La bibliothèque de l'interface réseau SQL n'a pas pu enregistrer le SPN.

À propos de l'authentification Windows sécurisée (Mise à jour à la demande de @Richard par @xpt)

L'authentification Windows se connecte à SQL Server avec les informations d'identification Windows sans spécifier d'ID utilisateur ni de mot de passe. C'est ce qu'on appelle une connexion de confiance pour sqlcmd ou ODBC; ou appelé Single-Sign-On pour go-mssqldb Pack de pilotes Go.

Depuis go-mssqldb 's readme dans github,

"ID utilisateur" - entrez l'ID utilisateur d'authentification SQL Server ou l'ID utilisateur WindowsAuthentication au format DOMAINE\Utilisateur. Sous Windows, si l'ID utilisateur est vide ou manquant, l'authentification unique est utilisée.

J'ai donc essayé les deux méthodes suivantes avec mon SQL Server 2008 R2 et les deux fonctionnent correctement :

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

Notez que l'utilisation de server=localhost échouerait, car il est important d'avoir le nom d'hôte correct, à partir de ce nom, le pilote construit le nom principal du service (SPN) SQL Server kerberos et ce nom doit correspondre à celui de SQL Server. J'ai utilisé un nom principal de service (SPN) approprié avec mon test pour que cela fonctionne.