如果我有一个SPA (单页应用程序-用BackboneJS开发的),并且想要有一个无状态的RESTful后端应用程序接口来处理它的数据。我喜欢第三方单点登录让用户的事情变得如此简单,因此会喜欢使用它。
但我知道,在这样的无状态环境中,身份验证是针对每个请求进行的吗?如果是这样,如果我正在使用第三方SSO,例如。GitHub,我不是每次都需要转到GitHub来验证用户吗?对于这种情况,最佳实践是什么?我相信这是一个非常常见的用例--我允许用户通过Google/GitHub或其他方式登录,然后从某个无状态的REST API获取数据
发布于 2013-08-20 23:18:03
免责声明:)
我已经为我的产品实现了这样一个东西,并分享了您的许多关注点和技术(特别是使用100%无状态REST后端的骨干SPA ),我可以告诉您我对此的想法,清楚地表明这不想成为“答案”,而是从由此产生的讨论中学习,因为我认为我在这个主题上也有相当多的东西需要学习。
首先,我认为你应该100%无国籍。100%,我的意思是100% :)不仅你的API层应该是无状态的,而且整个应用程序(当然除了客户端)都应该是无状态的。在不同的层上移动会话(例如,redis)只是稍微转移了一下问题,但并没有解决它。一切(尤其是伸缩)都会变得容易得多,稍后你会因为这个决定而感谢自己。
因此,是的,您需要对每个请求进行身份验证。但这并不意味着您必须每次都访问身份验证提供商。我学到的一件事是,允许用户通过FB/GitHub/任何方式(从现在开始,远程服务)进行身份验证只是一种缓解注册/登录痛苦的方法,除此之外没有别的。你仍然需要增加你的用户的个人数据库。当然,每个用户都将关联到一个“远程”用户,但在执行身份验证后不久,应用程序应该引用“您的”用户,而不是“远程”用户(例如。GitHub用户)。
实现
下面是我实现的内容:
POST /api/board?name=[a_name]&auth=[my_token]时,我知道是谁在调用,可以检查权限,并可以将新创建的实体board关联到正确的用户。authToken。此内标识有两个用途:- I can use it to call API methods on the remote service acting as the user
- I have the guarantee that the user is who it says it is, at least by the means of the remote service
remote_id: GitHub_abc123的用户,则创建该用户,否则加载该用户。假设这个用户有id: MyApp_def456。您还可以使用自己的逻辑创建一个表示用户MyApp_def456的authToken,并将其传递给客户端(cookies就可以了!!)备注
身份验证是在每次请求时执行的,这意味着您要处理散列和加密函数,这样定义的速度会很慢。现在,如果你使用20次迭代的bcrypt,这将杀死你的应用程序。当用户登录时,我使用它来存储用户的密码,但是对authToken使用一种不那么繁重的算法(我个人使用一种类似于SHA-256的散列)。这个令牌可以是短暂的(比方说少于平均破解它们的时间),并且在服务器机器上很容易计算。这里没有一个确切的答案。尝试不同的方法,衡量并决定。相反,我可以肯定的是,我更喜欢有这种问题,而不是会话问题。如果我需要计算更多的散列,或者更快,我会增加CPU能力。对于会话和集群环境,您会遇到内存问题、负载平衡和粘性会话问题,或者其他移动块(Redi)。
显然,HTTPS是绝对强制的,因为authToken总是作为参数传递的。
发布于 2013-08-16 18:43:02
我实现它的方式是在客户端(主干)和RESTful way服务器之间引入一个代理。代理与SSO一起管理用户的身份验证。因此,无需更改api和/或客户端/and服务器。下面是一个快速演示:
var http = require('http'),
httpProxy = require('http-proxy'),
express = require('express');
var proxy = new httpProxy.RoutingProxy();
var app = express();
function ensureAuthenticated(req, res, next) {
if (isLoggedIn) { return next(); }
res.redirect('/');
}
// This should be your (RESTful) webserver
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9000);
var isLoggedIn = false;
app.get('/', function(req, res){
console.log(isLoggedIn)
res.send('Logged in? ' + isLoggedIn);
});
app.get('/login', function(req, res){
isLoggedIn = true;
res.redirect('/');
});
app.get('/logout', function(req, res){
isLoggedIn = false;
res.redirect('/');
});
app.all('/api/*', ensureAuthenticated, function(req, res) {
return proxy.proxyRequest(req, res, {
host: 'localhost',
port: 9000
});
});
app.listen(8000);第一次访问该页面时,您已注销,任何对/api/something的呼叫都会被重定向至/。当您登录后(访问/login页面),所有对/api/*的请求都会通过代理被路由到侦听端口9000的When服务器。
特别是,当您设置app.all('/*', ...)时,对您的API服务器的所有调用都保持不变,但是增加了一个身份验证层。使用oauth扩展这个概念是很简单的(如果您使用的是node,请看一下passportjs )。
发布于 2013-08-20 01:30:15
您可以采用Facebook的JavaScript SDK中使用的方法。
This documentation page provides the quick-start to login vith Facebook for web.不是很深入,但解释了如何使用他们的方法的基本知识。但是,没有提到签名的可能性。
在为您的应用程序注册Facebook登录时,您将从Facebook上的应用程序仪表板获得应用程序密码。
当用户通过Facebook登录到您的应用程序时,您的JavaScript将获得一个身份验证对象。此对象包含签名。(如果在操控板中设置正确。)
您可以将此身份验证对象与对RESTful服务器的客户端调用一起提供,并在服务器上检查签名是否正确。这样,您就知道该用户已由facebook进行了身份验证,是此用户,并且已针对您的应用程序进行了身份验证。
This documentation page describes how to use the signed authentication.不会被标题中的“游戏”吓倒,它在任何web应用程序上都运行得很好。
您可以使用其他OAUTH提供程序,以与FB登录相同的方式实现一些功能,而不是只允许Facebook进行SSO。
使用danielepolencic建议的解决方案,但对其进行修改,以便在同一个node.js实例中使用另一台服务器进行登录,而不是使用代理。此服务通过提供程序执行OAUTH检查,并维护与客户端的会话。它向客户端发出一个带签名的令牌,生存时间很短。在生存时间结束之前,客户端必须请求新的令牌。
然后,为应用程序使用的登录实现一个功能类似于Facebook JavaScript SDK的客户端JavaScript。此函数既可以轮询新的令牌,也可以根据请求检索新的令牌,这对于该场景来说是最有效的。
客户端在每次请求时将此令牌提供给RESTful应用程序接口,服务器将检查签名。就像Facebook的SSO一样。
仍然有一个会话,但它可以在完全不同的机器上维护。此服务可以通过您的RESTful应用程序接口独立于服务器进行扩展。
但是,请记住,这种方法很容易受到中间人攻击和重放攻击。在没有https的情况下使用可能并不明智。
https://stackoverflow.com/questions/18168471
复制相似问题