Kết nối với Azure SQL bằng Service chính trong NodeJS, nhưng mã thông báo bị từ chối

Aug 16 2020

Tôi đang gặp sự cố khi đưa ứng dụng NodeJS của mình kết nối với Cơ sở dữ liệu Azure SQL bằng Service chính. Tuy nhiên, khi tôi cố gắng làm điều tương tự với C # Snippet, nó hoạt động tốt. Những gì tôi nhận thấy là các mã thông báo được trả về bởi auth trên cả hai ngôn ngữ là một chút khác nhau và nếu tôi lấy mã thông báo chính xác từ C # và mã hóa nó vào NodeJS, Kết nối SQL của tôi bây giờ thành công.

Lần đầu tiên tôi sử dụng ms-rest-azure để thực hiện xác thực và cung cấp clientId, tenantId và clientSecret của mình. Điều này trả lại một thông tin xác thực hợp lệ, từ đó tôi đang trích xuất accessToken.

Sau đó, tôi đang sử dụng tẻ nhạt để cố gắng kết nối với Azure SQL tại * .database.windows.net và cung cấp giá trị accessToken trong cấu hình.

Tôi vừa nhận được Đăng nhập không thành công đối với người dùng '<mã chính xác định mã thông báo>'

Tôi đang làm gì sai khi đăng nhập ms-rest-azure để cung cấp cho tôi mã thông báo bị Azure SQL từ chối? Một điều tôi thấy là mã thông báo đang hoạt động có đối tượng là database.windows.net , trong đó đối tượng từ ms-rest-azure là management.core.windows.net .

Tôi đã bị mắc kẹt trong vài ngày, nếu ai đó có bất kỳ manh mối nào ở đây thì thật tuyệt vời. Tài liệu về ms-rest-azure dường như không tồn tại và chỉ cung cấp cho bạn cách chạy đến các trang Bán hàng 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", 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);
        }
    });   
})

Trả lời

2 JimXu Aug 18 2020 at 02:34

khi chúng tôi sử dụng các chứng chỉ trong gói ms-rest-azuređể lấy mã thông báo, Theo mặc định, đối tượng của mã thông báo là https://management.core.windows.net/, nó chỉ có thể được sử dụng để gọi Azure rest api. Nếu chúng tôi muốn sử dụng mã thông báo Azure AD để kết nối sql, thì đối tượng của mã thông báo phải là https://database.windows.net/. Vì vậy, chúng tôi nên cập nhật mã được sử dụng để nhận mã thông báo là

msrestAzure.loginWithServicePrincipalSecret(
    clientId,
    clientSecret,
    tenantId,
    {
      tokenAudience: "https://database.windows.net/",
    },

Ví dụ

  1. Tạo một dịch vụ chính
az login
az ad sp create-for-rbac -n 'MyApp' --skip-assignment
  1. Định cấu hình cơ sở dữ liệu SQL

a. Sử dụng quản trị viên Azure Sql AD của bạn để kết nối Azure SQL vai SSMS

b. Thêm hiệu trưởng dịch vụ vào cơ sở dữ liệu bạn cần sử dụng

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);
  });

Để biết thêm chi tiết, vui lòng tham khảo tại đây