NodeJS에서 서비스 주체를 사용하여 Azure SQL에 연결하지만 토큰이 거부 됨
서비스 주체를 사용하여 NodeJS 애플리케이션을 Azure SQL Database에 연결하는 데 문제가 있습니다. 그러나 C # Snippet으로 동일한 작업을 시도하면 제대로 작동합니다. 내가 알아 차린 것은 두 언어의 인증에 의해 반환 된 토큰이 약간 다르며 C #에서 올바른 토큰을 가져 와서 NodeJS로 하드 코딩하면 이제 SQL 연결이 성공합니다.
먼저 ms-rest-azure를 사용하여 인증을 수행하고 clientId, tenantId 및 clientSecret을 제공합니다. 그러면 유효한 자격 증명이 반환되며 여기서 accessToken을 추출합니다.
그런 다음 * .database.windows.net에서 Azure SQL에 연결하고 구성에서 accessToken 값을 제공하는 데 지루한 방법을 사용하고 있습니다.
난 그냥 가져 '<토큰 확인 된 주요>'사용자가 로그인하지 못했습니다
Azure SQL에서 거부 된 토큰을 제공하기 위해 ms-rest-azure 로그인에서 뭘 잘못하고 있나요? 내가 본 한 가지는 작업 토큰에 database.windows.net 의 청중이 있다는 것입니다. ms-rest-azure의 대상은 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 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 데이터베이스 구성
ㅏ. Azure SQL AD 관리자를 사용하여 Azure SQL vai 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);
});
자세한 내용은 여기 를 참조 하십시오