我尝试阅读尽可能多的不同答案和帖子,但是我仍然不能完全满足自己的需求。我正在尝试找出处理用户身份验证,登录等的最佳方法(最有效,但大多数情况下更安全)。
我有一个运行在Express上的Node.js服务器;我有一个Angular.js网络应用程序;而且我有一个iOS应用。我使用Express /Node.js公开了RESTful API。
我读到的第一件事是使用cookie,并在服务器端(已哈希)和客户端(未哈希)存储会话ID /登录令牌。客户端将随每个请求一起传输此ID,服务器将对其进行哈希处理,解析并相应地处理请求。这感觉不是RESTful(不是一个大问题),但是更重要的是,我是否必须复制我的API:一个用于用户名/密码身份验证(例如,通过curl进行)和一个用于基于cookie的身份验证(例如,我的Web应用程序)?
另一个问题是:如果一个用户有多个连接,我该怎么办,例如,他们用两个浏览器(iPhone和iPad)登录。现在,我需要将其会话ID的存储空间存储为数组吗?
下一个想法是使用HTTP基本身份验证(带有SSL),这似乎很容易,但是不建议您这样做,因为您需要在每个请求中传递用户名和密码。如果要使用HTTPBasicAuth进行操作,我是否会将用户名和密码存储在cookie(或HTML本地存储)中以允许“记住我”功能?还是我可以将两者结合起来:对实际请求使用HTTP Basic Auth(发布新帖子等),而仅将存储在cookie中的会话ID用于序列中的初始日志/还记得我方面吗?
传输会话ID是否比仅传输用户密码更安全?怎么样?会话ID表面上将充当密码,因此对我来说,传输它与传输密码具有相同的安全性问题。
似乎所有平台都支持基本身份验证,这是理想的选择。主要的缺点似乎是每个请求都需要传输客户端身份验证数据。有没有办法减轻这个问题?
OAuth似乎对我的需求而言过于刻薄。我想我会失去执行curl命令来测试API的能力。OAuth与cookie方法相比有何改进?
您可能会说,我对可用的各种信息有些困惑,因此,如果您有一组适用于这种情况的良好链接,我希望阅读它们。我正在尝试找到适合所有平台的解决方案,但仍要尽可能地安全。另外,如果我的术语有误,请更正我,因为这会使我的搜索更加容易。
谢谢。
我一直在思考这个问题,并且已经有了一个主意。请告诉我这是否是愚蠢的/不安全的/任何反馈,因为我不确定这是否好。
当用户登录时,我们会生成一个随机的会话ID(加盐等)。这个 可选的 会话ID被发送给客户端,客户端可以选择将其存储(例如,存储在cookie中)。会话ID存储在数据库中。
然后,可以选择将该会话ID与每个请求一起作为HTTP身份验证标头或查询字符串 发送,或者如果需要,客户端可以只发送用户名和密码(这为我们提供了常规的REST API)。在服务器端,我们首先检查会话ID参数,如果不存在,则检查用户名/密码。如果两者都不存在,那就是错误。
在服务器上,我们检查会话ID是否与正确的用户名相关联。如果是,我们将完成请求。
每次用户登录时,我们都会创建一个新的会话ID或删除当前的会话ID,并将其与响应一起发送至登录请求。
我认为这可以让我在适当的情况下使用带有Basic Auth的常规REST API,并维护会话/记住我的功能。它不能解决多个登录问题,但是我认为这种方式应该可以。请告诉我。
我将使用基于令牌的身份验证,您可以在其中随每个请求发送令牌(自动)。您将必须登录一次,服务器将为您提供一个令牌,您可以将其用于发送每个请求。该令牌将添加到HTML标头中,因此您不必修改对浏览器的每个请求。
您可以在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', function (req, 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, status, headers, 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'); });
如果必须支持不支持本地存储的旧浏览器。您可以将其$window.sessionStorage与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'; });
和我们交换的authintercepter:
angular.module('myApp.authInterceptor', ['myApp.localStorage']).factory('authInterceptor', [ '$rootScope', '$q', 'localStorage', function ($rootScope, $q, 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/