You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用MSAL令牌认证连接Azure SQL数据库时出现“Login failed for user <token-identified principal>”错误

使用MSAL令牌认证连接Azure SQL数据库时出现“Login failed for user ”错误

你遇到的这个问题我之前帮好几个开发者排查过,大概率是几个容易忽略的配置或代码细节没处理对,咱们一步步来梳理解决:

一、先排查最关键的权限问题:身份是否能登录Azure SQL

这是90%的人踩坑的点!哪怕你拿到了合法的令牌,如果这个身份没在Azure SQL里被授权,肯定会报这个登录失败的错。
你得先登录Azure Portal找到你的SQL服务器,进入「查询编辑器」(需要你有SQL管理员权限),执行以下SQL语句:

  1. 先在服务器级别创建对应身份的登录名:
CREATE LOGIN [你的身份Client ID] FROM EXTERNAL PROVIDER;
  1. 切换到你的目标数据库,创建数据库用户并赋予必要权限:
USE [你的数据库名];
CREATE USER [你的身份Client ID] FROM EXTERNAL PROVIDER;
-- 给基础的读写权限,根据你的需求调整
ALTER ROLE db_datareader ADD MEMBER [你的身份Client ID];
ALTER ROLE db_datawriter ADD MEMBER [你的身份Client ID];

划重点:这里的「你的身份Client ID」要和你代码里用的一致——要么是之前ADAL里的service_principal_id,要么是现在MSAL里的AZURE_CLIENT_ID,必须完全匹配!

二、检查MSAL令牌的获取是否正确

你的代码里用ConfidentialClientApplication拿令牌,有几个细节要核对:

  1. 确认身份凭证是否对应
    你现在MSAL代码用的是环境变量里的AZURE_CLIENT_ID和AZURE_CLIENT_SECRET,这俩和之前ADAL从Key Vault拿的service_principal_id、service_principal_password是不是同一个服务主体?如果是不同的身份,那肯定没权限登录。
  2. 验证令牌有效性
    把你拿到的token['access_token']复制到微软的JWT.ms(这个工具不会上传数据,放心用)里解析,检查:
  • aud字段必须是https://database.windows.net/(完全匹配,不能多后缀)
  • iss字段是你的租户登录地址(比如https://sts.windows.net/你的租户ID/)
  • exp字段要在有效期内
  1. 简化令牌获取逻辑(推荐)
    你之前说不想硬编码密码,其实完全可以用DefaultAzureCredential直接拿令牌,不用绕MSAL的机密客户端——它支持托管身份、Azure CLI、环境变量等多种认证方式,更安全也更符合你的需求:
from azure.identity import DefaultAzureCredential

credential = DefaultAzureCredential()
# 直接获取Azure SQL的令牌
token = credential.get_token("https://database.windows.net/.default")

三、修正令牌的编码处理

你现在处理令牌的代码有点绕,而且如果令牌里有非ASCII字符可能会出问题。Azure SQL要求令牌是UTF-16LE编码的字节数组,前面加4字节的小端序长度,正确的写法应该是:

# 替换你原来的令牌处理代码
token_bytes = token["access_token"].encode("utf-16-le")
token_struct = struct.pack("<I", len(token_bytes)) + token_bytes

这样比你原来的循环拼接要简洁准确得多。

四、优化后的完整代码示例

结合上面的所有修正,给你一个更简洁安全的版本,用托管身份代替硬编码的服务主体密码:

import pyodbc
import os
import struct
from azure.keyvault.secrets import SecretClient
from azure.identity import DefaultAzureCredential

# 用DefaultAzureCredential,自动适配托管身份、环境变量等多种认证方式
credential = DefaultAzureCredential()

# 从Key Vault读取数据库配置
client = SecretClient(
    vault_url=os.environ.get('AZURE_KEY_VAULT_URI'),
    credential=credential
)
driver = client.get_secret('python-db-driver').value
server = client.get_secret('SqlServerName').value
database = client.get_secret('SqlDbName').value

# 基础连接字符串
connection_string = f'Driver={driver};SERVER={server};DATABASE={database}'

# 获取Azure SQL令牌
resource_uri = "https://database.windows.net/.default"
token = credential.get_token(resource_uri)

# 处理成Azure SQL要求的令牌格式
token_bytes = token.token.encode("utf-16-le")
token_struct = struct.pack("<I", len(token_bytes)) + token_bytes

# 尝试连接数据库
try:
    sql_conn = pyodbc.connect(connection_string, attrs_before={1256: token_struct})
    print("数据库连接成功!")
    # 测试执行SQL
    cursor = sql_conn.cursor()
    cursor.execute("SELECT @@VERSION")
    row = cursor.fetchone()
    print(f"SQL Server版本: {row[0]}")
except pyodbc.Error as e:
    print(f"连接失败: {str(e)}")
finally:
    if 'sql_conn' in locals():
        sql_conn.close()

最后再补几个检查点

  1. 如果你用的是用户分配的托管身份,记得在DefaultAzureCredential里指定它的Client ID:
credential = DefaultAzureCredential(managed_identity_client_id=os.environ['USER_ASSIGNED_MI_ID'])
  1. 确保SQL服务器的防火墙允许你的Kubernetes集群IP访问,或者开启「允许Azure服务和资源访问此服务器」
  2. 确认Key Vault的访问策略里,你的身份有Secret Get的权限

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 09:28:05