Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Ir com o driver do SQL Server não consegue se conectar com sucesso, falha de logon


Quero compartilhar minha experiência desenvolvendo um programa de banco de dados de linguagem Go de demonstração simples usando o SQL Server Express 2008. Acredito que as lições aprendidas a seguir se aplicarão a qualquer versão do SQL Server de 2008 e posterior.

Meu SQL Server Express foi instalado anteriormente com o default instância em vez de um named instância. Ele também foi instalado para usar a autenticação do Windows. Ambas as configurações foram exigidas por outro trabalho de desenvolvimento que faço. O outro trabalho que faço usa o SQL Server Express no mesmo PC que o aplicativo como mecanismo de banco de dados local. Eu esperava poder usar a autenticação do Windows com o SQL Server no meu aplicativo Go.

Procurando um driver e um pequeno programa de exemplo para usar com um SQL Server e Go local, essa pergunta surgiu na minha pesquisa. Pensei em adicionar um pouco de informação adicional e um programa de exemplo para ajudar os outros a começar e aprender com meus erros. Também achei este artigo GoLang and MSSQL Databases:An Example útil, especialmente depois de cometer erros suficientes para que eu o entendesse melhor.

A versão final do meu programa de teste é a seguinte:
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")
}

Na primeira vez que o aplicativo acima for executado, após as alterações necessárias nas configurações do SQL Server, ele gerará a seguinte saída. Como o banco de dados não existe na primeira vez que o programa é executado, você verá a mensagem de erro impressa. No entanto, nas próximas vezes em que for executado, o banco de dados existirá e a mensagem de erro quando o banco de dados for descartado não será exibida.
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

Instalando o pacote de driver do SQL Server

A primeira coisa que tive que fazer foi encontrar um pacote de driver de banco de dados que funcionasse com o SQL Server. Várias postagens de stackoverflow recomendadas github.com/denisenkom/go-mssqldb então foi isso que usou.

Para usar o github.com/denisenkom/go-mssqldb eu tive que primeiro recuperá-lo do repositório do github usando go get github.com/denisenkom/go-mssqldb da janela do shell de comando criada executando o Git Shell .

Git Shell é o shell do github que é instalado como parte da instalação do Git. Descobri que precisava executar o go get comando no Git Shell para que o go comando para encontrar o git aplicativo e acesse o repositório do github. Quando tentei executar o go get comando de um shell de comando normal, vi uma mensagem de erro indicando que o git comando não pôde ser encontrado.

Depois de instalar o go-mssqldb pacote, consegui executar meu aplicativo de exemplo e continuei executando um erro de tempo de execução do Open() . A saída da minha aplicação foi a seguinte:
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

Habilitando conexões TCP para SQL Server

Após algumas pesquisas, encontrei vários sites diferentes, todos indicando que o erro significava que minha instância do SQL Server não estava configurada para TCP/IP. As várias postagens indicaram que eu precisava usar o Sql Server Configuration Manager para habilitar o TCP/IP.

O que descobri é que na verdade existem dois lugares onde o TCP/IP precisa ser habilitado. Um deles era Client Protocols e isso de fato já estava ativado. No entanto, o outro era Protocols for MSSQLSERVER e nesse TCP/IP foi desabilitado. Então eu habilitei o TCP/IP no Protocols for MSSQLSERVER seção e reiniciou o serviço SQL Server usando o utilitário Serviço das Ferramentas Administrativas do Painel de Controle.

No entanto, eu ainda estava tendo problemas com qualquer tipo de consulta depois de usar sql.Open() . Eu estava vendo a saída do aplicativo que era uma variação do seguinte. A mensagem de erro era a mesma, no entanto, quando as chamadas de função tinham erros, podiam mudar de uma execução para outra. Tentei alterar a string de conexão especificada no sql.Open() sem resultados além de mensagens de erro diferentes.
starting app
  Error Exec db: create db -  driver: bad connection
  Error Exec db: create table -  driver: bad connection
ending app

Folheando mais, encontrei esta nota no repositório do github:

Problemas conhecidos

O mecanismo do SQL Server 2008 e 2008 R2 não pode manipular registros de logon quando a criptografia SSL não está desabilitada. Para corrigir o problema do SQL Server 2008 R2, instale o SQL Server 2008 R2 Service Pack 2. Para corrigir o problema do SQL Server 2008, instale o Microsoft SQL Server 2008 Service Pack 3 e o pacote de atualização cumulativa 3 para o SQL Server 2008 SP3. Mais informações:http://support.microsoft.com/kb/2653857

Então eu baixei as atualizações que eu nunca instalei. Enquanto esperava o download, pesquisei mais e encontrei a pasta contendo o executável real do SQL Server junto com o Log pasta contendo uma série de arquivos ERRORLOG , ERRORLOG.1 , etc

Os logs do SQL Server indicam que o usuário do SQL Server é necessário

Procurando no ERRORLOG encontrei um log de erros do SQL Server com os seguintes logs que forneceram a próxima peça do quebra-cabeça:
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]

Percebi então que o driver Go SQL Server não estava usando a autenticação do Windows, mas sim a autenticação do SQL Server. Eu tentei usar a autenticação do Windows especificando um user id= vazio no entanto, isso não parecia funcionar. Então, usando o sqlcmd utilitário, criei um usuário do SQL Server.
1> create login gouser with password='g0us3r';
2> go
1> create user gouser for login gouser;
2> go

Em seguida, baixei e instalei o Microsoft SQL Server Management Studio. Este é um utilitário diferente do SQL Server Configuration Manager. Usando isso, fiz duas coisas:(1) ativei a autenticação do SQL Server, bem como a autenticação do Windows e (2) forneci as permissões necessárias para meu novo usuário do SQL Server gouser . Este utilitário também forneceu uma interface de usuário agradável para navegar no SQL Server e seus vários bancos de dados.

Certifique-se de que o usuário SQL que você criou tenha permissões suficientes para que possa ser usado para se conectar ao SQL Server e criar um banco de dados.

Algumas considerações para usar a autenticação do Windows

Após uma pesquisa mais aprofundada, descobri que realmente poderia usar a autenticação do Windows, no entanto, o ID de usuário completamente qualificado e sua senha devem ser fornecidos. Para um ambiente usando o Active Directory com um nome de domínio de "AD", o ID de usuário totalmente qualificado seria "AD\userid" e para o host local seria "\userid". Ainda estou pesquisando para poder usar automaticamente as credenciais do usuário conectado no momento.

Depois de pesquisar ainda mais e encontrar assistência dos desenvolvedores do driver Go, a autenticação do Windows com o atual deve ser possível se o sql.Open() não inclui as informações do usuário que significam "id do usuário=;senha=;" não deve ser especificado.

No entanto, essa forma de autenticação automática do Windows em relação ao usuário atual só é permitida se a instância do SQL Server estiver usando Kerberos com um nome principal de serviço (SPN) válido. Se você executar uma reinicialização em sua instância do SQL Server e vir o log a seguir em seu arquivo ERRORLOG, o SQL Server não pôde inicializar com Kerberos.

2016-08-23 18:32:16.77 Servidor A biblioteca SQL Server Network Interface não pôde registrar o Service Principal Name (SPN) para o serviço SQL Server. Erro:0x54b, estado:3. Falha ao registrar um SPN pode fazer com que a autenticação integrada retorne para NTLM em vez de Kerberos. Esta é uma mensagem informativa. Ação adicional só é necessária se a autenticação Kerberos for exigida pelas políticas de autenticação.

Consulte também Como certificar-se de que você está usando a autenticação Kerberos ao criar uma conexão remota com uma instância do SQL Server 2005 que fornece algumas informações adicionais usando o setspn comando para corrigir o problema.

Consulte também A biblioteca do SQL Network Interface não pôde registrar o SPN.

Sobre a autenticação confiável do Windows (Atualizado conforme solicitação de @Richard por @xpt)

A Autenticação do Windows está fazendo logon no SQL Server com credencial do Windows sem especificar uma ID de usuário e senha. Isso é chamado de conexão confiável para sqlcmd ou ODBC; ou chamado Single-Sign-On para go-mssqldb Vá pacote de driver.

De go-mssqldb leia-me no github,

"user id" - insira o ID do usuário da Autenticação do SQL Server ou o ID do usuário do WindowsAuthentication no formato DOMAIN\User. No Windows, se o ID do usuário estiver vazio ou ausente, o Single Sign-On será usado.

Então, tentei as duas maneiras a seguir com meu SQL Server 2008 R2 e ambas estão funcionando bem:
condb, errdb := sql.Open("mssql", "server=MyServer;user id=;password=DONTCARE;")
condb, errdb := sql.Open("mssql", "server=MyServer;user id=;password=;")

Observe que o uso de server=localhost falharia, pois é importante ter o nome de host correto, a partir desse nome o driver está construindo o nome principal de serviço (SPN) do SQL Server kerberos e esse nome deve corresponder ao do SQL Server. Eu usei um nome principal de serviço (SPN) adequado com meu teste para que funcione.