我有一个Node.js服务器,在Express上运行;我有一个Angular.js网络应用程序我有一个iOS应用程序。我用Express / Node.js公开了一个RESTful API。
饼干
我读过的第一件事就是使用cookies,并在服务器端(散列)和客户端(未分配)上存储会话ID /登录令牌。客户端将会传送每个请求的id,服务器会将其哈希,解析并相应地处理该请求。这不是RESTful(不是一个巨大的问题),但更重要的是,我必须复制我的API:一个用于用户名/密码认证(例如通过curl完成),一个用于基于cookie的身份验证(例如我的网络应用程序)?
另一个问题是:如果我有一个用户的多个连接,我会做什么,例如他们登录在两个浏览器,一个iPhone和一个iPad。我的存储的会话ids需要现在是一个数组?
HTTP基本认证
接下来的想法是使用HTTP Basic Auth(使用SSL),这似乎很简单,但不推荐,因为您需要在每个请求中传输用户名和密码。如果我使用HTTP Basic Auth进行操作,那么我会将用户名和密码存储在cookie(或HTML本地存储)中,以允许“记住我”功能吗?或者我可以组合这两个:使用HTTP Basic Auth进行实际请求(发布新的帖子等),并且只使用存储在cookie中的会话ID来初始化日志/记住我的方面?
正在传送会话ID比只传送用户密码更安全吗?怎么样?
会话ID将作为表面上的密码,因此对于我来说,它将具有与发送密码相同的安全问题。
基本认证似乎在所有平台上得到支持,这是理想的。主要的缺点似乎是需要每个请求传输客户端认证数据。有办法缓解这个问题吗?
OAuth的
OAuth似乎对我的需要是过分的。我想我将失去卷曲命令测试我的API的能力。如何将OAuth作为Cookie方法的改进?
你可能会告诉我,我有点混淆了不同的信息,所以如果你有一套良好的链接 – 适用于这种情况 – 我很想读它们。我试图找到一个适合所有平台的解决方案,但仍然尽可能安全。此外,如果我的任何术语错误,请纠正我,因为它会使我的搜索变得更容易。
谢谢。
更新:
我一直在想这个问题,我有一个想法。请告诉我,如果这是愚蠢/不安全/任何反馈,因为我不知道这是否好。
当用户登录时,我们生成一个随机会话ID(盐渍等)。该可选会话ID被发送到客户端,客户端可以存储(例如,在Cookie中)如果他们选择;会话ID存储在数据库中。
然后,可以将此会话ID作为HTTP身份验证头或查询字符串作为每个请求发送,或者客户端可以根据需要发送用户名和密码(这给我们提供了常规的REST API)。在服务器端,我们首先检查一个session id参数,如果不存在,我们检查用户名/密码。如果没有 – 错误。
在服务器上,我们检查会话ID是否与正确的用户名相关联。如果是,我们完成请求。
每次用户登录时,我们创建一个新的会话ID或删除当前的会话ID,并将其发送给响应请求。
我认为这可以让我在适当的时候使用常规REST API和基本身份验证,并维护会话/记住我的功能。它不解决多个登录问题,但否则我认为这样应该会。请告诉我。
您可以在API中设置某些呼叫,以便它们始终需要令牌,而其他呼叫可能不受令牌保护。
对于Express,可以使用express-jwt(https://www.npmjs.org/package/express-jwt)
var expressJwt = require('express-jwt'); // Protect the /api routes with JWT app.use('/api',expressJwt({secret: secret})); app.use(express.json()); app.use(express.urlencoded());
app.post('/authenticate',function (req,res) { //if is invalid,return 401 if (!(req.body.username === 'john.doe' && req.body.password === 'foobar')) { res.send(401,'Wrong user or password'); return; } var profile = { first_name: 'John',last_name: 'Doe',email: 'john@doe.com',id: 123 }; // We are sending the profile inside the token var token = jwt.sign(profile,secret,{ expiresInMinutes: 60*5 }); res.json({ token: token }); });
而对于受保护的调用,以/ api开头的东西:
app.get('/api/restricted',res) { console.log('user ' + req.user.email + ' is calling /api/restricted'); res.json({ name: 'foo' }); });
在您的Angular应用程序中,您可以登录:
$http .post('/authenticate',$scope.user) .success(function (data,status,headers,config) { $window.sessionStorage.token = data.token; $scope.message = 'Welcome'; }) .error(function (data,config) { // Erase the token if the user fails to log in delete $window.sessionStorage.token; // Handle login errors here $scope.message = 'Error: Invalid user or password'; });
myApp.factory('authInterceptor',function ($rootScope,$q,$window) { return { request: function (config) { config.headers = config.headers || {}; if ($window.sessionStorage.token) { config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token; } return config; },response: function (response) { if (response.status === 401) { // handle the case where the user is not authenticated } return response || $q.when(response); } }; }); myApp.config(function ($httpProvider) { $httpProvider.interceptors.push('authInterceptor'); });
如果您必须支持不支持本地存储的旧浏览器。您可以使用像AmplifyJS(http://amplifyjs.com/)这样的库来交换$ window.sessionStorage。扩大例如使用任何本地存储可用。这将会像这样翻译:
if (data.status === 'OK') { //Save the data using Amplify.js localStorage.save('sessionToken',data.token); //This doesn't work on the file protocol or on some older browsers //$window.sessionStorage.token = data.token; $location.path('/pep'); } }).error(function (error) { // Erase the token if the user fails to log in localStorage.save('sessionToken',null); // Handle login errors here $scope.message = 'Error: Invalid user or password'; });
和我们交换的authinterpter:
angular.module('myApp.authInterceptor',['myApp.localStorage']).factory('authInterceptor',[ '$rootScope','$q','localStorage',localStorage) { return { request: function (config) { config.headers = config.headers || {}; config.headers.Authorization = 'Bearer ' + localStorage.retrieve('sessionToken'); return config; },response: function (response) { if (response.status === 401) { } return response || $q.when(response); } }; } ]);
您可以在本文中找到除AmplifyJS之外的所有内容:
http://blog.auth0.com/2014/01/07/angularjs-authentication-with-cookies-vs-token/