Chcę podzielić się swoim doświadczeniem w opracowywaniu prostego demonstracyjnego programu bazy danych w języku Go przy użyciu SQL Server Express 2008. Wierzę, że poniższe wnioski będą miały zastosowanie do każdej wersji SQL Server od 2008 roku.
Mój SQL Server Express był wcześniej zainstalowany z default
instancja zamiast named
instancja. Został również zainstalowany do korzystania z uwierzytelniania systemu Windows. Oba te ustawienia były wymagane przez inne prace programistyczne, które wykonuję. Inna praca, którą wykonuję, wykorzystuje SQL Server Express na tym samym komputerze, co aplikacja, jako lokalny silnik bazy danych. Spodziewałem się, że będę mógł używać uwierzytelniania Windows z SQL Server w mojej aplikacji Go.
Szukając sterownika i małego przykładowego programu do użycia z lokalnym serwerem SQL i Go, to pytanie pojawiło się podczas moich poszukiwań. Pomyślałem, aby dodać trochę dodatkowych informacji i przykładowy program, aby pomóc innym zacząć i uczyć się na moich błędach. Znalazłem również ten artykuł Bazy danych GoLang i MSSQL:przykład pomocny, szczególnie po popełnieniu wystarczających błędów, które lepiej zrozumiałem.
Ostateczna wersja mojego programu testowego wygląda następująco:
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")
}
Przy pierwszym uruchomieniu powyższej aplikacji po wprowadzeniu niezbędnych zmian w ustawieniach programu SQL Server wygeneruje ona następujące dane wyjściowe. Ponieważ baza danych nie istnieje przy pierwszym uruchomieniu programu, zostanie wyświetlony komunikat o błędzie. Jednak przy kolejnych uruchomieniach baza danych będzie istnieć, a komunikat o błędzie po usunięciu bazy danych nie zostanie wyświetlony.
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
Instalowanie pakietu sterownika SQL Server
Pierwszą rzeczą, którą musiałem zrobić, było znalezienie pakietu sterownika bazy danych, który będzie działał z SQL Server. Zaleca się kilka postów stackoverflow github.com/denisenkom/go-mssqldb
więc to właśnie zostało użyte.
Aby użyć github.com/denisenkom/go-mssqldb
pakiet Musiałem najpierw pobrać go z repozytorium github za pomocą go get github.com/denisenkom/go-mssqldb
z okna powłoki poleceń utworzonego przez uruchomienie Git Shell
.
Git Shell
to powłoka github, która jest instalowana w ramach instalacji Git. Zauważyłem, że muszę uruchomić go get
polecenie w Git Shell
w celu go
polecenie, aby znaleźć git
aplikacji i uzyskaj dostęp do repozytorium github. Kiedy próbowałem uruchomić go get
polecenie z normalnej powłoki poleceń Zobaczyłem komunikat o błędzie wskazujący, że git
nie można znaleźć polecenia.
Po zainstalowaniu go-mssqldb
pakiet Udało mi się uruchomić moją przykładową aplikację i ciągle napotykałem błąd w czasie wykonywania z Open()
. Wynik mojej aplikacji był następujący:
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
Włączanie połączeń TCP dla SQL Server
Po kilku poszukiwaniach znalazłem kilka różnych witryn, z których wszystkie wskazywały, że błąd oznaczał, że moja instancja SQL Server nie była skonfigurowana do obsługi TCP/IP. Różne wpisy wskazywały, że muszę użyć Sql Server Configuration Manager
aby włączyć TCP/IP.
Odkryłem, że w rzeczywistości istnieją dwa miejsca, w których należy włączyć protokół TCP/IP. Jednym z nich były Client Protocols
i to rzeczywiście było już włączone. Jednak drugim były Protocols for MSSQLSERVER
iw tym jeden TCP/IP był wyłączony. Więc włączyłem TCP/IP w Protocols for MSSQLSERVER
sekcji, a następnie ponownie uruchomiono usługę SQL Server za pomocą narzędzia Service w Narzędziach administracyjnych z Panelu sterowania.
Jednak nadal miałem problemy z wszelkiego rodzaju zapytaniami po użyciu sql.Open()
. Widziałem dane wyjściowe aplikacji, które były pewną odmianą następujących. Komunikat o błędzie był taki sam, jednak gdy wywołania funkcji zawierały błędy, mogły się zmieniać z jednego uruchomienia do następnego. Próbowałem zmienić ciąg połączenia określony w sql.Open()
bez wyników innych niż różne komunikaty o błędach.
starting app
Error Exec db: create db - driver: bad connection
Error Exec db: create table - driver: bad connection
ending app
Poszukując dalej, znalazłem tę notatkę w repozytorium github:
Znane problemy
Aparat SQL Server 2008 i 2008 R2 nie obsługuje rekordów logowania, gdy szyfrowanie SSL nie jest wyłączone. Aby rozwiązać problem z SQL Server 2008 R2, zainstaluj dodatek Service Pack 2 dla programu SQL Server 2008 R2. Aby rozwiązać problem dotyczący programu SQL Server 2008, zainstaluj dodatek Service Pack 3 dla programu Microsoft SQL Server 2008 i pakiet Cumulativeupdate 3 dla programu SQL Server 2008 z dodatkiem SP3. Więcej informacji:http://support.microsoft.com/kb/2653857
Pobrałem więc aktualizacje, których nigdy nie zainstalowałem. Czekając na pobranie, poszperałem dalej i znalazłem folder zawierający rzeczywisty plik wykonywalny SQL Server wraz z Log
folder zawierający serię plików ERRORLOG
, ERRORLOG.1
itp.
Dzienniki SQL Server wskazują, że użytkownik SQL Server jest wymagany
Szukam w ERRORLOG
plik Znalazłem dziennik błędów SQL Server z następującymi dziennikami, które stanowią kolejny element układanki:
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]
Wtedy zdałem sobie sprawę, że sterownik Go SQL Server nie używał uwierzytelniania Windows, ale zamiast tego używał uwierzytelniania SQL Server. Próbowałem użyć uwierzytelniania systemu Windows, określając pusty user id=
jednak to nie działało. Używając sqlcmd
narzędzie, stworzyłem użytkownika SQL Server.
1> create login gouser with password='g0us3r';
2> go
1> create user gouser for login gouser;
2> go
Następnie pobrałem i zainstalowałem Microsoft SQL Server Management Studio. Jest to inne narzędzie niż Menedżer konfiguracji programu SQL Server. Korzystając z tego zrobiłem dwie rzeczy:(1) włączyłem uwierzytelnianie SQL Server oraz uwierzytelnianie Windows i (2) zapewniłem niezbędne uprawnienia dla mojego nowego użytkownika SQL Server gouser
. To narzędzie zapewniało również ładny interfejs użytkownika do przeglądania SQL Server i jego różnych baz danych.
Upewnij się, że utworzony użytkownik SQL ma wystarczające uprawnienia, aby można go było używać do łączenia się z SQL Server i tworzenia bazy danych.
Niektóre uwagi dotyczące korzystania z uwierzytelniania systemu Windows
Po dalszych badaniach stwierdziłem, że faktycznie mógłbym użyć uwierzytelniania systemu Windows, jednak należy podać w pełni kwalifikowany identyfikator użytkownika i hasło. W środowisku korzystającym z usługi Active Directory z nazwą domeny „AD” w pełni kwalifikowanym identyfikatorem użytkownika będzie „AD\identyfikator użytkownika”, a hosta lokalnego — „\identyfikator użytkownika”. Nadal szukam możliwości automatycznego używania danych logowania aktualnie zalogowanego użytkownika.
Po dalszych badaniach i znalezieniu pomocy od twórców sterowników Go, uwierzytelnianie Windows z bieżącym powinno być możliwe, jeśli sql.Open()
nie zawiera informacji o użytkowniku oznaczających „identyfikator użytkownika=;hasło=;” nie powinno być określane.
Jednak ta forma automatycznego uwierzytelniania systemu Windows względem bieżącego użytkownika jest dozwolona tylko wtedy, gdy wystąpienie programu SQL Server korzysta z protokołu Kerberos z prawidłową główną nazwą usługi (SPN). Jeśli uruchomisz ponownie swoją instancję SQL Server i zobaczysz następujący dziennik w swoim pliku ERRORLOG, SQL Server nie mógł zainicjować przy użyciu Kerberos.
2016-08-23 18:32:16.77 Serwer Biblioteka interfejsu sieciowego programu SQL Server nie może zarejestrować głównej nazwy usługi (SPN) dla usługi SQL Server. Błąd:0x54b, stan:3. Niepowodzenie zarejestrowania nazwy SPN może spowodować, że zintegrowane uwierzytelnianie powróci do NTLM zamiast Kerberos. Jest to komunikat informacyjny. Dalsze działanie jest wymagane tylko wtedy, gdy uwierzytelnianie Kerberos jest wymagane przez zasady uwierzytelniania.
Zobacz także Jak upewnić się, że korzystasz z uwierzytelniania Kerberos podczas tworzenia zdalnego połączenia z instancją SQL Server 2005, która zapewnia dodatkowe informacje, jak również przy użyciu setspn
polecenie, aby naprawić problem.
Zobacz także Biblioteka interfejsu sieciowego SQL nie może zarejestrować SPN.
Informacje o zaufanym uwierzytelnianiu Windows (Zaktualizowany na żądanie @Richard przez @xpt)
Uwierzytelnianie systemu Windows polega na zalogowaniu się do programu SQL Server przy użyciu poświadczeń systemu Windows bez określania identyfikatora użytkownika i hasła. Nazywa się to zaufanym połączeniem dla sqlcmd
lub ODBC
; lub o nazwie Single-Sign-On dla go-mssqldb
Przejdź pakiet sterowników.
Z go-mssqldb
's readme na github,
„identyfikator użytkownika” — wprowadź identyfikator użytkownika uwierzytelniania programu SQL Server lub identyfikator użytkownika uwierzytelniania systemu Windows w formacie DOMENA\Użytkownik. W systemie Windows, jeśli identyfikator użytkownika jest pusty lub brakuje jednokrotnego logowania.
Wypróbowałem więc następujące dwa sposoby na moim SQL Server 2008 R2 i oba działają dobrze:
condb, errdb := sql.Open("mssql", "server=MyServer;user id=;password=DONTCARE;")
condb, errdb := sql.Open("mssql", "server=MyServer;user id=;password=;")
Należy zauważyć, że użycie server=localhost zakończy się niepowodzeniem, ponieważ ważne jest, aby mieć poprawną nazwę hosta, ponieważ z tej nazwy sterownik buduje główną nazwę usługi kerberos (SPN) programu SQL Server i ta nazwa musi być zgodna z nazwą programu SQL Server. Użyłem prawidłowej głównej nazwy usługi (SPN) w moim teście, więc to działa.