问题描述
我无法让我的NodeJS应用程序使用服务主体连接到Azure sql数据库。但是,当我尝试对C#代码段执行相同的操作时,效果很好。我注意到的是,两种语言的auth返回的令牌都有点不同,如果我从C#中获取正确的令牌并将其硬编码到NodeJS中,则sql连接现在可以成功。
我首先使用ms-rest-azure进行身份验证,并提供我的clientId,tenantId和clientSecret。这将返回一个有效的凭证,我将从该凭证中提取accesstoken。
然后,我使用乏味的方法尝试通过* .database.windows.net连接到Azure sql,并在配置中提供accesstoken值。
我只是获得用户'
在ms-rest-azure登录中给我一个被Azure sql拒绝的令牌时,我在做什么错?我看到的一件事是,工作令牌的受众为 database.windows.net ,其中-来自ms-rest-azure的用户为 management.core.windows.net 。
我已经被困了几天,如果有人在这里有任何很棒的线索。 ms-rest-azure上的文档似乎很不存在,只是为您提供了Azure销售页面的解决方法。
const msRestAzure = require('ms-rest-azure');
const { reject } = require('async');
let clientSecret = "xxx";
let serverName = "xxx.database.windows.net";
let databaseName = "xxx";
let clientId = "xxx";
let tenantId = "xxx";
azureCredentials = msRestAzure.loginWithServicePrincipalSecret(clientId,clientSecret,tenantId,function(err,credentials) {
if (err) return console.log(err);
credentials.getToken((err,results) => {
if(err) return reject(err);
let accesstoken = results.accesstoken;
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var config = {
server: serverName,authentication: {
type: 'azure-active-directory-access-token',options: {
token: accesstoken
}
},options: {
debug: {
packet: true,data: true,payload: true,token: false,log: true
},database: databaseName,encrypt: true
}
};
var connection = new Connection(config);
connection.connect();
connection.on('connect',function(err) {
if(err) {
console.log(err);
}
executeStatement();
}
);
connection.on('debug',function(text) {
console.log(text);
}
);
function executeStatement() {
request = new Request("select * from Text",rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
connection.close();
});
request.on('row',function(columns) {
columns.forEach(function(column) {
if (column.value === null) {
console.log('NULL');
} else {
console.log(column.value);
}
});
});
request.on('done',function(rowCount,more) {
console.log(rowCount + ' rows returned');
});
connection.execsql(request);
}
});
})
解决方法
当我们使用软件包ms-rest-azure
中的证书获取令牌时,默认情况下,令牌的访问者为https://management.core.windows.net/
,它只能用于调用Azure rest api。如果我们要使用Azure AD令牌连接sql,则令牌的访问者应为https://database.windows.net/
。因此,我们应该更新用于获取令牌的代码
msrestAzure.loginWithServicePrincipalSecret(
clientId,clientSecret,tenantId,{
tokenAudience: "https://database.windows.net/",},
例如
- 创建服务主体
az login
az ad sp create-for-rbac -n 'MyApp' --skip-assignment
- 配置SQL数据库
a。 Use your Azure Sql AD admin to connect Azure SQL vai SSMS
b。将服务主体添加到您需要使用的数据库中
create user [<Azure_AD_principal_name>] from external provider
ALTER ROLE db_owner ADD MEMBER [<Azure_AD_principal_name>]
- 代码
var msrestAzure = require("ms-rest-azure");
var { Connection,Request } = require("tedious");
let clientSecret = "xxx";
let serverName = "xxx.database.windows.net";
let databaseName = "xxx";
let clientId = "xxx";
let tenantId = "xxx";
async function getConnect() {
// way for Azure Service Principal
let databaseCredentials = await msrestAzure.loginWithServicePrincipalSecret(
clientId,);
// getting access token
let databaseAccessToken = await new Promise((resolve,reject) => {
databaseCredentials.getToken((err,results) => {
if (err) return reject(err);
resolve(results.accessToken);
});
});
var config = {
server: serverName,authentication: {
type: "azure-active-directory-access-token",options: {
token: databaseAccessToken,options: {
debug: {
packet: true,data: true,payload: true,token: false,log: true,database: databaseName,encrypt: true,};
var connection = new Connection(config);
connection.connect();
connection.on("connect",function (err) {
if (err) {
console.log(err);
}
executeStatement(connection);
});
connection.on("debug",function (text) {
console.log(text);
});
}
function executeStatement(connection) {
request = new Request("select * from CSVTest",function (err,rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + " rows");
}
connection.close();
});
request.on("row",function (columns) {
columns.forEach(function (column) {
if (column.value === null) {
console.log("NULL");
} else {
console.log(column.value);
}
});
});
request.on("done",function (rowCount,more) {
console.log(rowCount + " rows returned");
});
connection.execSql(request);
}
getConnect()
.then(() => {
console.log("run successfully");
})
.catch((err) => {
console.log(err);
});
有关更多详细信息,请参阅here