最快的问题总结:我一直在努力。我已经在这里发表了我对我的问题的最简洁的陈述。这个问题引用了stack上的这个问题和openapi生成器上的另一个问题。
初始问题:
我将以这个问题开始,然后提供背景。
问:我如何修改自动生成的OpenAPI node express代码(如下部分所示),以访问存储在对受保护路由的请求的头(或参数列表)中的API键?
我不想在路由endpoint修改路由中间件。我认为正确的做法是使用额外的中间中间件,拦截API密钥,检查数据库中的用户角色,然后传递到链中的下一个中间件,只有当用户有适当的角色时。
那么,如何编写此中间 API 密钥到角色检查器中间件,在受保护路由上注册的中间件堆栈中使用它,以及如何访问请求标头中的 API 密钥?
我本以为自动生成的节点express代码将使其清晰可见并可访问。然而,似乎缺少一个易于使用的应用程序层钩子,它访问API密钥(以允许对该密钥进行RBAC检查)。此外,我还没有在网上找到足够的文档来说明OpenAPI应用程序管道的这一部分。
我甚至买了这本书,希望能找到这些问题的答案。本书没有充分讨论围绕安全性、API 密钥处理等的应用层问题。
上下文:我使用OpenAPI生成器来创建我的node express服务和我的客户端库。
我有一个可用的服务和一个可用的MaterialUI/React客户端应用程序,它可以访问该服务上的GET、POST、PUT和DEL路由。然而,我增加了认证和授权用户的能力。我现在使用API密钥机制。最终我也会用oauth2。然而,我将把这个问题集中在API键上。
目前,对于需要密钥存在的路由,该服务接受任何 API 密钥(甚至是硬编码密钥)。身份验证/授权的方法对设计人员保持开放。
服务代码需要访问密钥,以便允许我实现RBAC。
我决定使用JWT(Java Web Tokens)作为用户注册和登录过程的一部分。实际上,我已按照本教程将 JWT 注册和登录添加到我的应用程序中。我还在我的应用程序中添加了 passport(但我还没有真正使用它),因为我认为我会将其用作登录过程的一部分(它为 JWT 以外的协议提供无缝支持)。
所有这些(JWT注册/登录)似乎也有效。我的 Web 应用程序是一个反应/材料 UI 应用程序。我已经修改了它以允许用户注册和登录。这行得通。服务器将 JWT 返回到 Web 应用程序。我的想法是,我将使用 JWT 中的令牌作为 API 密钥(用于对受保护服务路由的后续请求)。
我不知道如何访问服务本身的密钥。自动生成的服务使用的openAPI库只是隐藏了应用程序中间件的那一部分。
这是自动生成的服务类。我添加了猫鼬,因为我将我的用户信息存储在后端 mongo 数据库中。
注意,这个类使用了express-openapi-validator
const http = require('http');
const fs = require('fs');
const path = require('path');
const swaggerUI = require('swagger-ui-express');
const jsYaml = require('js-yaml');
const express = require('express');
const cors = require('cors');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const { OpenApiValidator } = require('express-openapi-validator');
const passport = require('passport');
const logger = require('./logger');
const config = require('./config').getConfig();
const { MongoDatabase } = require('./database');
const { OpenApiValidator } = require('express-openapi-validator');
class ExpressServer {
constructor(port, openApiYaml, keys) {
this.port = port;
this.app = express();
this.openApiPath = openApiYaml;
try {
this.schema = jsYaml.safeLoad(fs.readFileSync(openApiYaml));
} catch (e) {
logger.error('failed to start Express Server', e.message);
}
this.setupMiddleware();
}
setupMiddleware() {
// this.setupAllowedMedia();
this.app.use(cors());
this.app.use(bodyParser.json({ limit: '14MB' }));
this.app.use(express.json());
this.app.use(express.urlencoded({ extended: false }));
this.app.use(cookieParser());
// Simple test to see that the server is up and responding
this.app.get('/hello', (req, res) => res.send(`Hello World. path: ${this.openApiPath}`));
// Send the openapi document *AS GENERATED BY THE GENERATOR*
this.app.get('/openapi', (req, res) => res.sendFile((path.join(__dirname, 'api', 'openapi.yaml'))));
// View the openapi document in a visual interface. Should be able to test from this page
this.app.use('/api-docs', swaggerUI.serve, swaggerUI.setup(this.schema));
this.app.get('/login-redirect', (req, res) => {
res.status(200);
res.json(req.query);
});
this.app.get('/oauth2-redirect.html', (req, res) => {
res.status(200);
res.json(req.query);
});
}
launch() {
new OpenApiValidator({
apiSpec: this.openApiPath,
operationHandlers: path.join(__dirname),
fileUploader: { dest: config.FILE_UPLOAD_PATH },
}).install(this.app)
.catch((e) => console.log(e))
.then(() => {
// eslint-disable-next-line no-unused-vars
this.app.use((err, req, res, next) => {
// format errors
res.status(err.status || 500).json({
message: err.message || err,
errors: err.errors || '',
});
});
http.createServer(this.app).listen(this.port);
console.log(`Listening on port ${this.port}`);
});
MongoDatabase.connect(config.db.mongo_url);
}
async close() {
if (this.server !== undefined) {
await this.server.close();
console.log(`Server on port ${this.port} shut down`);
}
}
}
以下是示例服务路线(用于删除订单):
const deleteOrder = ({ orderId }) => new Promise(
async (resolve, reject) => {
try {
resolve(Service.successResponse({
orderId,
}));
} catch (e) {
reject(Service.rejectResponse(
e.message || 'Invalid input',
e.status || 405,
));
}
},
);
我希望能够使用API键来搜索我的mongo数据库,以便查看用户是否具有适当的角色(我不希望任何经过身份验证的用户都能够删除订单)。注意,上面的deleteOrder方法不能访问这个API键。此外,这不是进行检查的正确位置。正如在上面的问题中提到的,进行这种检查的正确位置是在中间应用程序中间件中,该中间件截获API密钥,并根据数据库中的用户信息进行检查,以确保用户具有适当的角色。
以下代码片段是在我的注册和登录路由上调用的当前工作函数。请注意,它们在密码验证时处理 JWT 创建过程。
const createUser = ({ user }) => new Promise(
async (resolve, reject) => {
console.log('createUser: ', user);
const saltHash = jwtUtils.genPassword(user.password);
const newUser = new User({
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
phone: user.phone,
userStatus: user.userStatus,
hash: saltHash.hash,
salt: saltHash.salt,
});
try {
newUser.save()
.then((usr) => {
console.log('createUser: Added ', usr);
// eslint-disable-next-line no-underscore-dangle
const jwt = jwtUtils.issueJWT(usr);
const rtn = {
_id: usr._id.toString(),
firstName: usr.firstName,
lastName: usr.lastName,
token: jwt.token,
expiresIn: jwt.expires,
};
resolve(Service.successResponse(rtn));
});
} catch (err) {
console.log('createUser: Error ', err);
reject(Service.rejectResponse(
err.message || 'Invalid input',
err.status || 405,
));
}
},
);
const loginUser = ({ username, password }) => new Promise(
async (resolve, reject) => {
console.log('loginUser: ', username);
try {
User.findOne({ email: username })
.then((usr) => {
if (!usr) {
reject(Service.rejectResponse(
'Invalid user',
401,
));
}
const isValid = jwtUtils.validPassword(password, usr.hash, usr.salt);
if (isValid) {
const jwt = jwtUtils.issueJWT(usr);
const rtn = {
_id: usr._id.toString(),
firstName: usr.firstName,
lastName: usr.lastName,
token: jwt.token,
expiresIn: jwt.expires,
};
resolve(Service.successResponse(rtn));
} else {
reject(Service.rejectResponse(
'Invalid password',
401,
));
}
})
.catch((err) => {
reject(Service.rejectResponse(
err.message || 'Invalid input',
err.status || 405,
));
});
} catch (e) {
reject(Service.rejectResponse(
e.message || 'Invalid input',
e.status || 405,
));
}
},
);
最后,这里是客户端的一段示例代码,显示了API键如何进入服务请求。注意,它目前正在对该密钥进行硬编码,并且尚未使用JWT令牌作为密钥。然而,这项服务对这个硬编码的密钥非常满意。我正在努力解决这个问题!这就是我问题的本质!
let defaultClient = ApiClient.instance
defaultClient.basePath = window._env_.API_URL
let api_key = defaultClient.authentications['api_key'];
api_key.apiKey = 'YOUR API KEY';
let apiInstance = new VideoApi(defaultClient);
export function requestGetVideos(test, id) {
let p = new Promise((resolve, reject) => {
apiInstance.getVideoInventory((error, data, response) => {
if (error) {
console.error(error);
reject(error);
} else {
resolve({"data": data, "response": response});
}
});
})
return p;
}
后续细节。
查看关于验证器的文档,我试图找到如何将令牌验证器添加到由openapi-validator设置的路由中。
我找到了神奇的调味料。express-openapi-validator 中的优秀文档有所帮助。请参阅下面的验证安全性部分。
new OpenApiValidator({
apiSpec: this.openApiPath,
operationHandlers: path.join(__dirname),
fileUploader: { dest: config.FILE_UPLOAD_PATH },
validateSecurity: {
handlers: {
// eslint-disable-next-line no-unused-vars
ApiKeyAuth: (req, scopes, schema) => {
console.log('Yuck - This is not called.');
// eslint-disable-next-line no-throw-literal
throw { status: 401, message: 'sorry' };
},
// eslint-disable-next-line no-unused-vars
OpenID: async (req, scopes, schema) => {
console.log('abc');
// eslint-disable-next-line no-throw-literal
throw { status: 403, message: 'forbidden' };
},
// eslint-disable-next-line no-unused-vars
OAuth2: async (req, scopes, schema) => {
console.log('abcd');
// eslint-disable-next-line no-throw-literal
throw { status: 403, message: 'forbidden' };
},
// eslint-disable-next-line no-unused-vars
api_key: async (req, scopes, schema) => {
console.log('abcde: ', req.headers);
// eslint-disable-next-line no-throw-literal
throw { status: 403, message: 'forbidden' };
},
},
},
}).install(this.app)
我正在维护一个Java应用程序,我们在其中不断添加新特性(api中的更改)。我想使用OpenAPI作为记录api的一种方式。我看到两种思想流派: 编写代码,使用一些注释来生成OpenAPI规范。 虽然两者看起来都很好,但服务器代码只是简单地存档了,然后需要大量的手动插入服务。虽然这似乎罚款作为一个一次性成本,然后下次我更新界面,它似乎对我来说唯一的两个选项是 再次生成它们,重新执行所有手动接线 手
我试图生成一个API客户端从v2 swagger文件openapi生成器cli。为此,我使用openapi生成器cli的docker容器,它将其版本报告为4.1.0-SNAPSHOT。 代码生成使用以下选项: 我还尝试将选项设置为true。 但是,生成的服务类不使用装饰器进行注释。因此,在我的组件中导入它们并在组件的构造函数中添加服务后,我无法使用它们。这就是我的组件的样子: 失败,因为userS
我正在使用openapi生成器生成服务器存根python代码。一切正常,但是,每次我修改OpenAPI规范(yaml文件),代码生成器都会覆盖整个代码,甚至是定制的代码(控制器)。我想开发一个增量工作流,如果我对规范进行了更改,生成器将只修改处理该部分代码的代码。 如果我能够执行规范并拥有一个增量工作流,那就太好了。 我使用的是openapi生成器版本3.3。4. 我试图修改控制器并删除,但每次我
我正在使用openapi生成器maven插件的4.3.1版本在Java11中生成SpringBoot服务器。 对于PUT请求,我希望能够在成功时将URI返回到创建/更新的对象,并且在不成功时返回带有问题信息的纯文本。 我的API的json对于PUT请求有以下内容: 生成的API: 然而,该方法的返回类型是
我正在开发一个Spring Boot应用程序的后端,该应用程序使用OpenAPI和Swagger通过模式为前端应用程序提供接口。yml文件。当对控制器进行更改时,我们使用swagger ui获取api文档JSON,使用在线swagger编辑器将其转换为yaml,并将结果粘贴到模式中。yml文件 现在,我想让它自动化,这样我们就可以调用一个maven任务来自动生成yaml文件,但我找不到任何mave
我正在使用composer-rest-server生成rest API。我正在使用Passport-jwt验证rest api。在composer rest服务器中,我们在cookie中获得access_token。
Jboot 内置了一个简易的代码生成器,可以用来生成model层和Service层的基础代码,在生成代码之前,请先配置jboot.properties关于数据库相关的配置信息,Jboot 代码生成器会通过该配置去链接数据库。 jboot.datasource.type=mysql jboot.datasource.url=jdbc:mysql://127.0.0.1:3306/jbootdemo
遵照此规范,在实际操作中,有许多重复。接下来推荐一款专为本规范量身定做的代码生成器 Laravel 5.x Scaffold Generator。 本扩展支持 5.1 ~ 5.5 版本的 Laravel。 只需要一个命令: 即可生成: $ php artisan make:scaffold Projects --schema="name:string:index,description:text: