node.js - 异步编程 - 如果文件不存在则创建文件然后读取它
问题描述
我正在用 Node.js 编写一个与 AD FS 对话的身份验证微服务,但在正确的时间读取文件时遇到问题。我对 Node.js 和异步编程还是很陌生。我正在尝试使用 Passport 模块,它需要 AD FS 服务器的证书。过去,我只是手动下载证书,并将其放在程序可以读取的位置,但我想摆脱这种情况,并在第一次按需创建它。我可以在玩具程序中毫无问题地创建证书,但是当我尝试将其合并到更大的程序中时,我遇到了文件尚不可用的异步/竞争条件问题。我本质上是在尝试做:
//pseudo code
if (file does not exist) {
create_file();
return read_file();
}
else {
return read_file();
}
问题出在 getAdfsCertificate 函数中。我不知道如何等待写入完成。我想我需要做
writeCertificate(x509CertificateStr, adfsCertificateFilename)
.then(() => return fs.readFileSync(adfsServerCertFilename));
但我不确定语法,我认为我正在将 Promise 与 async/await 混合和匹配。
我的代码片段如下:
这是请求证书的地方
passport.use(new Strategy(
{
jwt: {
algorithm: 'RS256'
},
cert: getAdfsCertificate(process.env.ADFS_SERVER, adfsServerCertFilename),
path: '',
realm: process.env.REALM,
homeRealm: '',
identityProviderUrl: process.env.IDENTITY_PROVIDER_URL,
thumbprint: process.env.ADFS_THUMBPRINT
},
function (profile, done) {
// console.log("Auth with", profile);
if (!profile.upn) {
return done(new Error("No upn found"), null);
}
done(null, profile);
}
));
这些是获取证书并在必要时创建的相关功能:
async function getAdfsCertificate(adfsServer, adfsCertificateFilename) {
let fileExists = await fs.promises.access(adfsCertificateFilename).then(() => true).catch(() => false);
if (!fileExists) {
console.log('certificate file does not exist');
console.log('fetching FederationMetadata from AD FS Server');
var xmlResponse = await getFederationMetadataXml(adfsServer);
console.log('extracting x509 certificate from xml');
var x509CertificateStr = await parseX509FromFederationMetaData(xmlResponse);
console.log('saving x509 certificate to file');
await writeCertificate(x509CertificateStr, adfsCertificateFilename);
}
console.log('retrieving certificate from file');
return fs.readFileSync(adfsServerCertFilename);
}
async function getFederationMetadataXml(adfsServer) {
try {
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
let response = await axios.get('https://' + adfsServer + '/FederationMetadata/2007-06/FederationMetadata.xml', { httpsAgent });
return response.data;
} catch (error) {
console.log('Error getting AD FS FederationMetadData ', error);
}
}
async function parseX509FromFederationMetaData(metadataXml) {
try {
var xmlDoc = libxml.parseXmlString(metadataXml);
var x509cert = xmlDoc.get('/a:EntityDescriptor/b:Signature/b:KeyInfo/b:X509Data/b:X509Certificate',
{
a: 'urn:oasis:names:tc:SAML:2.0:metadata',
b: 'http://www.w3.org/2000/09/xmldsig#'
});
return x509cert.text();
} catch (error) {
console.log('Error parsing x509 from FederationMetadata', error);
}
}
async function writeCertificate(str, filename) {
try {
// wrap long lines of certificate to 64 character lines
const line_length = 64;
const line_count = Math.ceil(str.length / line_length)
let writestream = fs.createWriteStream(filename);
let offset = 0;
writestream.write('-----BEGIN CERTIFICATE-----\n');
for (let i = 0; i < line_count; ++i) {
writestream.write(str.substr(offset, line_length));
writestream.write('\n');
offset += line_length;
}
writestream.write('-----END CERTIFICATE-----\n');
writestream.end();
} catch (error) {
console.log('Error while writing certificate file', error);
}
}
解决方案
我不确定回答您自己问题的协议,尤其是当它包含来自评论的反馈时。我想发布一个“答案”,所以我可以用完全格式化的代码来说明。
我摆脱了对 fs.promises.access 的异步调用,并将其替换为 fs.existsSync 函数。我还放入了一个 else 块以防止 fs.readSync 在那里被调用。最后,我使用 writeCertificate 承诺中的“.then”来执行 fs.readSync。
async function getAdfsCertificate(adfsServer, adfsCertificateFilename) {
if (! fs.existsSync(adfsCertificateFilename)) {
// first time app is run, certificate does not exist
// download and format appropriately
console.log('certificate file does not exist');
console.log('fetching FederationMetadata from AD FS Server');
var xmlResponse = await getFederationMetadataXml(adfsServer);
console.log('Extracting x509 certificate from xml');
var x509CertificateStr = await parseX509FromFederationMetaData(xmlResponse);
console.log('saving x509 certificate to file');
writeCertificate(x509CertificateStr, adfsCertificateFilename)
.then(() => { return fs.readFileSync(adfsServerCertFilename);});
} else {
// certificate exists, read it
console.log('retrieving certificate from file');
return fs.readFileSync(adfsServerCertFilename);
}
}
修改 writeCertificate 以返回一个承诺。我认为将其声明为“异步”就足够了。
function writeCertificate(str, filename) {
return new Promise(function(resolve, reject) {
// wrap long lines of certificate to 64 character lines
const line_length = 64;
const line_count = Math.ceil(str.length / line_length)
let f = fs.createWriteStream(filename);
let offset = 0;
f.write('-----BEGIN CERTIFICATE-----\n');
for (let i = 0; i < line_count; ++i) {
f.write(str.substr(offset, line_length));
f.write('\n');
offset += line_length;
}
f.write('-----END CERTIFICATE-----\n');
f.end();
f.on('finish', resolve);
f.on('error', reject);
});
}
推荐阅读
- javascript - 使用 JavaScript Flow,如何将允许的值限制为类型的键?
- sql - SQL 在查询 SSMS 期间处理字符串中的特殊字符
- swift - SwiftUI:如何在没有属性包装器的情况下添加手势状态?
- android - Android Studio 3.6,无法在设备上安装应用
- excel - 使用 vba 从一个工作簿复制数据并粘贴到另一个工作簿
- sympy - 如何在 SymPy 中求解包含 Sum 的方程?
- c++ - 第三方库中的宏冲突
- embedded - Coverity 静态分析将 char 或 numbers 视为 C 中的 int
- extjs - 本地网格过滤器
- xml - 如何限制 Excel 加载项的受支持应用程序集以发布到 AppSource?