Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Wiele aplikacji musi dynamicznie budować łańcuchy połączeń, zamiast przechowywać je jako statyczne wartości konfiguracyjne. Wybierz podejście dopasowane do Twojego wdrożenia:
- Zmienne środowiskowe: Najlepsze dla kontenerów, CI/CD i aplikacji 12-składnikowych. Proste i szeroko wspierane.
- Pliki konfiguracyjne JSON/YAML: Najlepsze rozwiązanie dla aplikacji z wieloma środowiskami (deweloperskim, przedprodukcyjnym, produkcyjnym), które wymagają ustrukturyzowanej konfiguracji.
- Azure Key Vault: Najlepszy do wdrożeń produkcyjnych, gdzie sekrety muszą być centralnie zarządzane i audytowane.
- Klasa Builder: Najlepsze dla bibliotek lub frameworków, które muszą konstruować ciągi połączeń z wejścia użytkownika z automatycznym escapingiem.
Podstawowa konstrukcja strun
Użyj struny f
Struny f są powszechnym podejściem do szybkich skryptów i prototypów. Unikaj tego wzorca, gdy wartości pochodzą z danych wejściowych użytkownika, ponieważ złośliwa wartość taka jak mydb;Server=evil.com może zmienić cel połączenia:
import mssql_python
server = "<server>.database.windows.net"
database = "<database>"
connection_string = f"Server={server};Database={database};Authentication=ActiveDirectoryDefault;Encrypt=yes;"
conn = mssql_python.connect(connection_string)
Użyj funkcji join
Podejście to join rozdziela pary klucz-wartość na wywołanie funkcji w stylu słownikowym, które jest łatwiejsze do odczytania i utrzymania niż długi ciąg f. Dodatkowo automatycznie odfiltrowuje wartości None, dzięki czemu możesz przekazywać opcjonalne parametry bez dodatkowej logiki warunkowej:
def build_connection_string(**kwargs) -> str:
"""Build connection string from keyword arguments."""
return ";".join(f"{key}={value}" for key, value in kwargs.items() if value is not None)
conn_str = build_connection_string(
Server="<server>.database.windows.net",
Database="<database>",
Authentication="ActiveDirectoryDefault",
Encrypt="yes"
)
conn = mssql_python.connect(conn_str)
Klasa konstruktora parametrów połączenia
Klasa builder zapewnia API płynne z automatycznym escapingiem. To podejście jest przydatne w bibliotekach lub aplikacjach wielodzierżawczych, gdzie parametry połączenia pochodzą z różnych źródeł:
import mssql_python
class ConnectionStringBuilder:
"""Builder for SQL Server connection strings."""
def __init__(self):
self._params = {}
def server(self, value: str) -> "ConnectionStringBuilder":
self._params["Server"] = value
return self
def database(self, value: str) -> "ConnectionStringBuilder":
self._params["Database"] = value
return self
def trusted_connection(self) -> "ConnectionStringBuilder":
self._params["Trusted_Connection"] = "yes"
return self
def sql_auth(self, username: str, password: str) -> "ConnectionStringBuilder":
self._params["UID"] = username
self._params["PWD"] = password
return self
def entra_default(self) -> "ConnectionStringBuilder":
self._params["Authentication"] = "ActiveDirectoryDefault"
return self
def entra_msi(self, client_id: str = None) -> "ConnectionStringBuilder":
self._params["Authentication"] = "ActiveDirectoryMSI"
if client_id:
self._params["UID"] = client_id
return self
def encrypt(self, value: bool = True) -> "ConnectionStringBuilder":
self._params["Encrypt"] = "yes" if value else "no"
return self
def trust_server_certificate(self, value: bool = True) -> "ConnectionStringBuilder":
self._params["TrustServerCertificate"] = "yes" if value else "no"
return self
def connect_timeout(self, seconds: int) -> "ConnectionStringBuilder":
self._timeout = seconds
return self
def build(self) -> str:
"""Build the connection string."""
return ";".join(f"{k}={v}" for k, v in self._params.items())
def connect(self) -> mssql_python.Connection:
"""Build and connect."""
return mssql_python.connect(self.build(), timeout=getattr(self, '_timeout', 0))
# Usage examples
# Microsoft Entra authentication (recommended)
conn = (ConnectionStringBuilder()
.server("<server>.database.windows.net")
.database("<database>")
.entra_default()
.encrypt()
.connect())
# Azure with managed identity
conn = (ConnectionStringBuilder()
.server("<server>.database.windows.net")
.database("<database>")
.entra_msi()
.encrypt()
.connect())
Konfiguracja oparta na środowisku
Ze zmiennych środowiskowych
Odczytywanie parametrów połączenia ze zmiennych środowiskowych pozwala trzymać dane uwierzytelniające poza kodem źródłowym i sprawdza się zarówno w lokalnym środowisku programistycznym, jak i w kontenerach oraz potokach CI/CD. Funkcja sprawdza, którą metodę uwierzytelniania użyć na podstawie ustawionych zmiennych:
import os
import mssql_python
def get_connection_from_env() -> mssql_python.Connection:
"""Build connection from environment variables."""
server = os.environ.get("SQL_SERVER")
database = os.environ.get("SQL_DATABASE")
if not server or not database:
raise ValueError("SQL_SERVER and SQL_DATABASE environment variables required")
# Check for authentication method
if os.environ.get("SQL_USE_MSI", "").lower() == "true":
# Azure Managed Identity
conn_str = f"Server={server};Database={database};Authentication=ActiveDirectoryMSI;Encrypt=yes;"
elif os.environ.get("SQL_TRUSTED_CONNECTION", "").lower() == "true":
# Windows authentication
conn_str = f"Server={server};Database={database};Trusted_Connection=yes;Encrypt=yes;"
else:
# SQL authentication
username = os.environ.get("SQL_USERNAME")
password = os.environ.get("SQL_PASSWORD")
if not username or not password:
raise ValueError("SQL_USERNAME and SQL_PASSWORD required for SQL authentication")
conn_str = f"Server={server};Database={database};UID={username};PWD={password};Encrypt=yes;"
return mssql_python.connect(conn_str)
# Usage
conn = get_connection_from_env()
Z użyciem python-dotenv
Pakiet python-dotenv wczytuje pary klucz-wartość z pliku .env do zmiennych środowiskowych, dzięki czemu kod korzysta z poświadczeń w ten sam sposób zarówno w środowisku lokalnym, jak i produkcyjnym. Plik .env nie trafia do systemu kontroli wersji (dodaj go do .gitignore), a wdrożone środowiska udostępniają te same zmienne za pośrednictwem platformowego magazynu sekretów.
Zainstaluj za pomocą pip install python-dotenv.
Utwórz plik .env w katalogu głównym projektu z parametrami połączenia:
# .env - add this file to .gitignore
SQL_SERVER=<server>.database.windows.net
SQL_DATABASE=<database>
SQL_USE_MSI=true
Następnie załaduj i użyj tych wartości w swoim skrypcie:
from dotenv import load_dotenv
import os
import mssql_python
# Load .env file into os.environ (no-op if the file doesn't exist)
load_dotenv()
server = os.getenv("SQL_SERVER")
database = os.getenv("SQL_DATABASE")
if not server or not database:
raise ValueError("SQL_SERVER and SQL_DATABASE must be set in .env or as environment variables")
use_msi = os.getenv("SQL_USE_MSI", "false").lower() == "true"
if use_msi:
conn_str = f"Server={server};Database={database};Authentication=ActiveDirectoryMSI;Encrypt=yes;"
else:
conn_str = f"Server={server};Database={database};Authentication=ActiveDirectoryDefault;Encrypt=yes;"
conn = mssql_python.connect(conn_str)
Wskazówka
load_dotenv() nie nadpisuje zmiennych, które już są ustawione w środowisku. W środowisku produkcyjnym ustaw te same nazwy zmiennych za pośrednictwem swojej platformy (na przykład w ustawieniach aplikacji w usłudze App Service lub jako zmienne środowiskowe kontenera) i całkowicie pomiń plik .env.
Konfiguracja oparta na plikach
Z konfiguracji JSON
Plik konfiguracyjny JSON pozwala definiować ustawienia połączeń dla wielu środowisk (program, staging, produkcja) w jednym miejscu. Funkcja odczytuje plik, wybiera środowisko docelowe i buduje string połączenia na podstawie ustawień strukturalnych:
import json
import io
import mssql_python
def load_connection_from_json(config_file, environment: str = "development") -> str:
"""Load connection settings from a JSON config file or file-like object."""
config = json.load(config_file)
env_config = config.get(environment, {})
db_config = env_config.get("database", {})
params = {
"Server": db_config.get("server"),
"Database": db_config.get("database"),
"Encrypt": "yes" if db_config.get("encrypt", True) else "no",
}
auth_type = db_config.get("authentication", "sql")
if auth_type == "msi":
params["Authentication"] = "ActiveDirectoryMSI"
elif auth_type == "default":
params["Authentication"] = "ActiveDirectoryDefault"
elif auth_type == "windows":
params["Trusted_Connection"] = "yes"
else:
params["UID"] = db_config.get("username")
params["PWD"] = db_config.get("password")
return ";".join(f"{k}={v}" for k, v in params.items() if v)
# Example: load from an inline JSON config (in production, use open("config.json"))
sample_config = json.dumps({
"development": {
"database": {
"server": "localhost",
"database": "devdb",
"authentication": "windows",
"encrypt": False
}
},
"production": {
"database": {
"server": "prod.database.windows.net",
"database": "proddb",
"authentication": "msi",
"encrypt": True
}
}
})
conn_str = load_connection_from_json(io.StringIO(sample_config), "production")
print(f"Connection string: {conn_str}")
Z konfiguracji YAML
Pliki konfiguracyjne YAML są czytelną alternatywą dla JSON. Są one powszechnie używane w projektach Python i wdrożeniach Kubernetes. To podejście odczytuje ustawienia połączenia z ustrukturyzowanego pliku YAML i buduje parametry połączenia na podstawie typu uwierzytelniania zdefiniowanego w konfiguracji.
Zainstaluj pakiet, uruchamiając pip install pyyaml.
Utwórz plik database.yml w swoim projekcie:
database:
server: <server>.database.windows.net
name: <database>
authentication: msi
encrypt: true
Następnie załaduj i użyj tych ustawień w swoim skrypcie:
import yaml
import mssql_python
def load_from_yaml(config_path: str) -> mssql_python.Connection:
"""Load connection from YAML config."""
with open(config_path) as f:
config = yaml.safe_load(f)
db = config["database"]
parts = [
f"Server={db['server']}",
f"Database={db['name']}",
]
if db.get("trusted_connection"):
parts.append("Trusted_Connection=yes")
elif db.get("authentication") == "msi":
parts.append("Authentication=ActiveDirectoryMSI")
else:
parts.append(f"UID={db['username']}")
parts.append(f"PWD={db['password']}")
if db.get("encrypt", True):
parts.append("Encrypt=yes")
if db.get("trust_server_certificate"):
parts.append("TrustServerCertificate=yes")
return mssql_python.connect(";".join(parts))
conn = load_from_yaml("database.yml")
Integracja usługi Azure Key Vault
W przypadku wdrożeń produkcyjnych przechowywaj dane połączeń w Azure Key Vault, a nie w plikach konfiguracyjnych czy zmiennych środowiskowych. Key Vault zapewnia scentralizowane zarządzanie tajemnicami, audyt dostępu oraz automatyczną rotację. Zainstaluj wymagane pakiety, uruchamiając pip install azure-keyvault-secrets azure-identity. Pełny przegląd znajdziesz w Quickstart: Azure Key Vault secret client library for Python.
import os
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
import mssql_python
def get_connection_from_keyvault(vault_url: str) -> mssql_python.Connection:
"""Build connection using secrets from Azure Key Vault."""
credential = DefaultAzureCredential()
client = SecretClient(vault_url=vault_url, credential=credential)
server = client.get_secret("sql-server").value
database = client.get_secret("sql-database").value
username = client.get_secret("sql-username").value
password = client.get_secret("sql-password").value
conn_str = f"Server={server};Database={database};UID={username};PWD={password};Encrypt=yes;"
return mssql_python.connect(conn_str)
vault_url = os.environ.get("AZURE_KEY_VAULT_URL")
if vault_url:
conn = get_connection_from_keyvault(vault_url)
Obsługa znaków specjalnych
Poprzedź średniki i nawiasy klamrowe znakiem ucieczki
Musisz uciekać z wartości parametry połączenia zawierające specjalne znaki. Owij wartość w napierkach {} i podwaj wszystkie wewnętrzne zamknięcia }:
def escape_value(value: str) -> str:
"""Escape special characters in connection string values."""
if ";" in value or "{" in value or "}" in value:
# Wrap in braces and escape internal braces
value = value.replace("}", "}}")
return "{" + value + "}"
return value
# Password with semicolon
password = "my;complex;password"
escaped_password = escape_value(password) # {my;complex;password}
conn_str = f"Server=<server>;Database=<database>;UID=<login>;PWD={escaped_password};"
Budowniczy z automatycznym ucieczką
Ta klasa builder automatycznie opakowuje każdą wartość, więc wywołujący nie muszą pamiętać reguł ucieczki. Używaj go, gdy parametry połączenia pochodzą z zewnętrznych wejść, takich jak formularze użytkownika, API konfiguracyjne lub tajne magazyny, gdzie wartości mogą zawierać średniki lub nawiasy:
class SafeConnectionStringBuilder:
"""Connection string builder with automatic escaping."""
SPECIAL_CHARS = {";", "{", "}"}
def __init__(self):
self._params = {}
def _escape(self, value: str) -> str:
if any(c in value for c in self.SPECIAL_CHARS):
value = value.replace("}", "}}")
return "{" + value + "}"
return value
def set(self, key: str, value: str) -> "SafeConnectionStringBuilder":
self._params[key] = self._escape(value)
return self
def build(self) -> str:
return ";".join(f"{k}={v}" for k, v in self._params.items())
# Safely handles special characters
builder = SafeConnectionStringBuilder()
builder.set("Server", "<server>.database.windows.net")
builder.set("Database", "<database>")
builder.set("PWD", "pass;word{with}special") # Automatically escaped
conn_str = builder.build()
Validation
Zanim użyjesz w aplikacji dynamicznie tworzonego ciągu połączenia, sprawdź, czy rzeczywiście umożliwia nawiązanie połączenia. Ta funkcja pomocnicza wykonuje próbne proste zapytanie i zwraca wartość logiczną:
import mssql_python
def validate_connection_string(conn_str: str) -> bool:
"""Validate a connection string by attempting to connect."""
try:
conn = mssql_python.connect(conn_str)
cursor = conn.cursor()
cursor.execute("SELECT 1")
cursor.fetchone()
conn.close()
return True
except mssql_python.Error as e:
print(f"Connection failed: {e}")
return False
# Test before using
conn_str = "Server=<server>.database.windows.net;Database=<database>;Authentication=ActiveDirectoryDefault;Encrypt=yes;"
if validate_connection_string(conn_str):
print("Connection string is valid")