tsc*_*ela 199 rest node.js express passport.js
我开始使用node.js,express和mongodb规划REST API.API为网站(公共和私人区域)以及稍后的移动应用程序提供数据.该前端将与AngularJS一起开发.
有些日子我读了很多关于保护REST API的内容,但我没有找到最终的解决方案.据我所知,使用HTTPS提供基本的安全性.但是我如何在该用例中保护API:
只允许网站/应用的访问者/用户获取网站/应用的公共区域的数据
只允许经过身份验证和授权的用户获取私有区域的数据(并且只有用户授予权限的数据)
目前我认为只允许具有活动会话的用户使用API.为了授权用户,我将使用护照并获得许可,我需要为自己实施一些内容.全部在HTTPS之上.
有人可以提供一些最佳实践或经验吗?我的"架构"是否缺乏?
Gab*_*mas 172
我有同样的问题你描述.我正在构建的网站可以通过手机和浏览器访问,因此我需要一个api来允许用户注册,登录和执行一些特定的任务.此外,我需要支持可伸缩性,在不同的进程/机器上运行相同的代码.
因为用户可以创建资源(也就是POST/PUT操作),所以您需要保护您的api.您可以使用oauth或者您可以构建自己的解决方案,但请记住,如果密码很容易被发现,所有解决方案都可能被破坏.基本思想是使用用户名,密码和令牌(即apitoken)对用户进行身份验证.可以使用node-uuid生成此apitoken,并且可以使用pbkdf2对密码进行哈希处理
然后,您需要在某处保存会话.如果将其保存在普通对象的内存中,如果您终止服务器并再次重新启动它,会话将被销毁.此外,这不可扩展.如果您使用haproxy在计算机之间进行负载平衡,或者您只是使用工作程序,则此会话状态将存储在单个进程中,因此如果同一用户被重定向到另一个进程/计算机,则需要再次进行身份验证.因此,您需要将会话存储在一个公共位置.这通常使用redis完成.
当用户通过身份验证(用户名+密码+ apitoken)时,会为会话生成另一个令牌,即accessstoken.再次,使用node-uuid.向用户发送accesstoken和userid.userid(key)和accesstoken(value)以redis存储,并且到期时间为1h.
现在,每次用户使用其余的api进行任何操作时,都需要发送用户标识和accessstoken.
如果您允许用户使用其余api进行注册,则需要创建一个带有管理员apitoken的管理员帐户并将其存储在移动应用程序中(加密用户名+密码+ apitoken),因为新用户不会有apitoken时他们报名参加.
网络也使用这个api,但你不需要使用apitokens.您可以将express与redis商店一起使用,或者使用上述相同的技术,但绕过apitoken检查并在cookie中返回用户ID + accesstoken.
如果您有私有区域,则在进行身份验证时将用户名与允许的用户进行比较.您还可以将角色应用于用户.
摘要:

没有apitoken的替代方案是使用HTTPS并在Authorization标头中发送用户名和密码,并在redis中缓存用户名.
cib*_*en1 20
根据(我希望如此)我接受的答案,我想提供这个代码作为提出问题的结构解决方案.(您可以非常轻松地自定义它).
// ------------------------------------------------------
// server.js
// .......................................................
// requires
var fs = require('fs');
var express = require('express');
var myBusinessLogic = require('../businessLogic/businessLogic.js');
// .......................................................
// security options
/*
1. Generate a self-signed certificate-key pair
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out certificate.pem
2. Import them to a keystore (some programs use a keystore)
keytool -importcert -file certificate.pem -keystore my.keystore
*/
var securityOptions = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('certificate.pem'),
requestCert: true
};
// .......................................................
// create the secure server (HTTPS)
var app = express();
var secureServer = require('https').createServer(securityOptions, app);
// ------------------------------------------------------
// helper functions for auth
// .............................................
// true if req == GET /login
function isGETLogin (req) {
if (req.path != "/login") { return false; }
if ( req.method != "GET" ) { return false; }
return true;
} // ()
// .............................................
// your auth policy here:
// true if req does have permissions
// (you may check here permissions and roles
// allowed to access the REST action depending
// on the URI being accessed)
function reqHasPermission (req) {
// decode req.accessToken, extract
// supposed fields there: userId:roleId:expiryTime
// and check them
// for the moment we do a very rigorous check
if (req.headers.accessToken != "you-are-welcome") {
return false;
}
return true;
} // ()
// ------------------------------------------------------
// install a function to transparently perform the auth check
// of incoming request, BEFORE they are actually invoked
app.use (function(req, res, next) {
if (! isGETLogin (req) ) {
if (! reqHasPermission (req) ){
res.writeHead(401); // unauthorized
res.end();
return; // don't call next()
}
} else {
console.log (" * is a login request ");
}
next(); // continue processing the request
});
// ------------------------------------------------------
// copy everything in the req body to req.body
app.use (function(req, res, next) {
var data='';
req.setEncoding('utf8');
req.on('data', function(chunk) {
data += chunk;
});
req.on('end', function() {
req.body = data;
next();
});
});
// ------------------------------------------------------
// REST requests
// ------------------------------------------------------
// .......................................................
// authenticating method
// GET /login?user=xxx&password=yyy
app.get('/login', function(req, res){
var user = req.query.user;
var password = req.query.password;
// rigorous auth check of user-passwrod
if (user != "foobar" || password != "1234") {
res.writeHead(403); // forbidden
} else {
// OK: create an access token with fields user, role and expiry time, hash it
// and put it on a response header field
res.setHeader ('accessToken', "you-are-welcome");
res.writeHead(200);
}
res.end();
});
// .......................................................
// "regular" methods (just an example)
// newBook()
// PUT /book
app.put('/book', function (req,res){
var bookData = JSON.parse (req.body);
myBusinessLogic.newBook(bookData, function (err) {
if (err) {
res.writeHead(409);
res.end();
return;
}
// no error:
res.writeHead(200);
res.end();
});
});
// .......................................................
// "main()"
secureServer.listen (8081);
Run Code Online (Sandbox Code Playgroud)
可以使用curl测试此服务器:
echo "---- first: do login "
curl -v "https://localhost:8081/login?user=foobar&password=1234" --cacert certificate.pem
# now, in a real case, you should copy the accessToken received before, in the following request
echo "---- new book"
curl -X POST -d '{"id": "12341324", "author": "Herman Melville", "title": "Moby-Dick"}' "https://localhost:8081/book" --cacert certificate.pem --header "accessToken: you-are-welcome"
Run Code Online (Sandbox Code Playgroud)
小智 12
我刚刚完成了一个示例应用程序,它以一种非常基本但清晰的方式执行此操作.它使用mongoose和mongodb来存储用户和护照以进行身份验证管理.
https://github.com/Khelldar/Angular-Express-Train-Seed
在SO上有很多关于REST auth模式的问题.这些与您的问题最相关:
基本上,您需要选择使用API密钥(最不安全,因为密钥可能被未经授权的用户发现),应用密钥和令牌组合(中等),或完整的OAuth实施(最安全).
如果您想保护您的应用程序,那么您绝对应该首先使用 HTTPS 而不是 HTTP,这可以确保在您和用户之间创建安全通道,以防止嗅探来回发送给用户的数据并有助于保留数据交换了机密。
您可以使用 JWTs (JSON Web Tokens) 来保护 RESTful APIs,这与服务器端会话相比有很多好处,主要好处是:
1- 更具可扩展性,因为您的 API 服务器不必为每个用户维护会话(当您有许多会话时,这可能是一个很大的负担)
2- JWT 是自包含的,并且具有定义用户角色的声明,例如他可以访问的内容和发布日期和到期日(之后 JWT 将无效)
3- 更容易跨负载平衡器处理,如果您有多个 API 服务器,因为您不必共享会话数据,也不必配置服务器将会话路由到同一服务器,只要带有 JWT 的请求命中任何服务器,就可以对其进行身份验证& 授权
4- 减轻您的数据库压力,并且您不必为每个请求不断存储和检索会话 ID 和数据
5- 如果您使用强密钥对 JWT 进行签名,则 JWT 不会被篡改,因此您可以信任随请求发送的 JWT 中的声明,而无需检查用户会话以及他是否获得授权,您只需检查 JWT,然后您就可以知道该用户可以做什么以及可以做什么。
许多库提供了在大多数编程语言中创建和验证 JWT 的简单方法,例如:在 node.js 中最流行的一种是jsonwebtoken
由于 REST API 通常旨在保持服务器无状态,因此 JWT 与该概念更兼容,因为每个请求都使用自包含(JWT) 的授权令牌发送,而与会话相比,服务器不必跟踪用户会话服务器是有状态的,以便它记住用户及其角色,但是,会话也被广泛使用并具有其优点,您可以根据需要进行搜索。
需要注意的一件重要事情是,您必须使用 HTTPS 将 JWT 安全地交付给客户端并将其保存在安全的地方(例如在本地存储中)。
您可以从此链接了解有关 JWT 的更多信息
| 归档时间: |
|
| 查看次数: |
109471 次 |
| 最近记录: |