การเชื่อมต่อกับ Azure SQL โดยใช้ Service Principal ใน NodeJS แต่โทเค็นถูกปฏิเสธ
ฉันมีปัญหาในการรับแอปพลิเคชัน NodeJS เพื่อเชื่อมต่อกับฐานข้อมูล Azure SQL โดยใช้ Service Principal อย่างไรก็ตามเมื่อฉันพยายามทำสิ่งเดียวกันกับ C # Snippet มันก็ใช้ได้ดี สิ่งที่ฉันสังเกตเห็นคือโทเค็นที่ส่งคืนโดย auth ในทั้งสองภาษานั้นแตกต่างกันเล็กน้อยและถ้าฉันใช้โทเค็นที่ถูกต้องจาก C # และฮาร์ดโค้ดลงใน NodeJS ตอนนี้การเชื่อมต่อ SQL ของฉันจะสำเร็จ
ฉันใช้ ms-rest-azure เป็นครั้งแรกในการตรวจสอบสิทธิ์ของฉันและระบุ clientId, tenantId และ clientSecret สิ่งนี้จะคืนกลับข้อมูลรับรองที่ถูกต้องซึ่งฉันกำลังแยก accessToken
จากนั้นฉันใช้ความน่าเบื่อในการพยายามเชื่อมต่อกับ Azure SQL ที่ * .database.windows.net และให้ค่า accessToken ในการกำหนดค่า
ฉันเพิ่งเข้าสู่ระบบล้มเหลวสำหรับผู้ใช้ '<โทเค็นระบุหลัก>'
ฉันทำอะไรผิดในการเข้าสู่ระบบ ms-rest-azure เพื่อให้โทเค็นที่ Azure SQL ปฏิเสธ สิ่งหนึ่งที่ผมเห็นก็คือว่าโทเค็นการทำงานที่มีผู้ชมdatabase.windows.netที่-เป็นหนึ่งจาก MS-ส่วนที่เหลือเป็นสีฟ้าmanagement.core.windows.net
ฉันติดอยู่สองสามวันถ้าใครมีเบาะแสที่นี่จะดีมาก เอกสารประกอบเกี่ยวกับ ms-rest-azure ดูเหมือนจะไม่มีอยู่จริงและเพียงแค่ให้การรันบนหน้า Azure Sales แก่คุณ
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", 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);
}
});
})
คำตอบ
เมื่อเราใช้ใบรับรองในแพ็คเกจms-rest-azure
เพื่อรับโทเค็นโดยค่าเริ่มต้นผู้ชมของโทเค็นคือhttps://management.core.windows.net/
มันสามารถใช้เพื่อเรียก Azure rest api ได้ ถ้าเราต้องการที่จะใช้ Azure โฆษณา token เชื่อมต่อกับ 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
ก. ใช้ผู้ดูแลระบบ Azure Sql AD ของคุณเพื่อเชื่อมต่อ Azure SQL กับ SSMS
ข. เพิ่มหลักบริการในฐานข้อมูลที่คุณต้องการใช้
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,
clientSecret,
tenantId,
{
tokenAudience: "https://database.windows.net/",
},
);
// 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);
});

สำหรับรายละเอียดเพิ่มเติมโปรดดูที่นี่