【问题标题】:Pass Azure AD Token for Azure SQL DB in pre-connection arguments to SQLAlchemy create_engine()在连接前参数中将 Azure SQL DB 的 Azure AD 令牌传递给 SQLAlchemy create_engine()
【发布时间】:2019-12-03 05:17:23
【问题描述】:

我想通过带有 AD 令牌的 SQLAlchemy 连接到 Azure SQL DB。

我已经关注https://github.com/felipefandrade/azuresqlspn,并且可以通过此方法成功连接。但是,我想扩展它并使用 SQLAlchemy 来管理连接。

from os import environ
import struct
import adal
from sqlalchemy import create_engine
import pyodbc

clientSecret = environ.get('clientSecret')
clientID = environ.get('clientID')
tenantID =  environ.get('tenantID')
authorityHostUrl = "https://login.microsoftonline.com"
authority_url = authorityHostUrl + '/' + tenantID
resource = "https://database.windows.net/"
context = adal.AuthenticationContext(authority_url, api_version=None)

token = context.acquire_token_with_client_credentials(
    resource,
    clientID,
    clientSecret)


tokenb = bytes(token["accessToken"], "UTF-8")
exptoken = b''

for i in tokenb:
    exptoken += bytes({i})
    exptoken += bytes(1)

tokenstruct = struct.pack("=i", len(exptoken)) + exptoken

driver = "Driver={ODBC Driver 17 for SQL Server}"
server = ";SERVER={0}".format(environ.get('server'))
database = ";DATABASE={0}".format(environ.get('database'))

connString = driver + server + database

SQL_COPT_SS_ACCESS_TOKEN = 1256
conn = pyodbc.connect(connString, attrs_before={SQL_COPT_SS_ACCESS_TOKEN:tokenstruct})

cursor = conn.cursor()
cursor.execute("SELECT TOP 20 ID, Name FROM [Table1]")
row = cursor.fetchone()
print("######")
print("Using pyodbc directly - This works")
while row:
    print (str(row[0]) + " " + str(row[1]))
    row = cursor.fetchone()

print("#####")
print("Connecting via sqlalchemy - This doesn't work")
SAconnString = "mssql+pyodbc://<server>.database.windows.net/<database>?driver=ODBC+Driver+17+for+SQL+Server"
db = create_engine(SAconnString, connect_args={'attrs_before': {SQL_COPT_SS_ACCESS_TOKEN:tokenstruct}})

SAconn = db.connect()
result = SAconn.execute("SELECT TOP 20 ID, Name FROM [Table1]")
for row in result:
    print(row['ID'] + " " + row['Name'])

如代码中所述,使用pyodbc.connect() 方法将起作用。但是,使用 SQLAlchemy 我得到错误FA005] [Microsoft][ODBC Driver 17 for SQL Server]Cannot use Access Token with any of the following options: Authentication, Integrated Security, User, Password。在我看来,connect_args 选项正在传递令牌,但也有其他选项。如何让 SQLAlchemy 正确地将 AD Token 传递给 pyodbc?

【问题讨论】:

    标签: python sqlalchemy azure-sql-database pyodbc


    【解决方案1】:

    需要使用urllib 显式发送 PyODBC 连接字符串。

    connString = driver + server + database
    params = urllib.parse.quote(connString)
    db = create_engine("mssql+pyodbc:///?odbc_connect={0}".format(params), connect_args={'attrs_before': {SQL_COPT_SS_ACCESS_TOKEN:tokenstruct}})
    

    【讨论】:

      【解决方案2】:

      这段代码使用 SQLAlchemy 怎么样:

      import sqlalchemy
      import urllib
      import pyodbc
      
      
      params = urllib.parse.quote_plus("Driver={ODBC Driver 13 for SQL Server};Server=tcp:***.database.windows.net,1433;Database=Mydatabase;Uid=ServerAdmin@***;Pwd=***;Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30;")
      
      engine = sqlalchemy.create_engine("mssql+pyodbc:///?odbc_connect=%s" % params, connect_args={'attrs_before': {SQL_COPT_SS_ACCESS_TOKEN:tokenstruct}})
      conn=engine.connect()
      print(conn)
      
      result = conn.execute("select * from tb1")
      for row in result:
          print(str(row[0]) + " " + str(row[1]))
      conn.close()
      

      我从门户获取连接字符串:

      希望这会有所帮助。

      【讨论】:

      • 这真的有效吗? Microsoft documentation 声明您在使用令牌身份验证时不能发送 UID、PWD 或 TrustedConnection。否则,是的,我发现您需要使用urllib.parse.quote()
      • 这里的挑战不是在代码中做所有事情并避免使用密码/用户吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-30
      • 1970-01-01
      • 2019-07-26
      • 2018-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多