使用Angular.js和iOS客户端对Node.js应用程序进行身份验证

时间:2014-01-23 06:00:45

标签: ios node.js angularjs authentication

我试图尽可能多地阅读不同的答案和帖子,但我仍然不能完全找到符合我需求的解决方案。我正试图找出最好的(最有效的,但大多数是更安全的)方式来处理用户身份验证,登录等。

我有一台运行在Express上的Node.js服务器;我有一个Angular.js网络应用程序;我有一个iOS应用程序。我使用Express / Node.js公开了一个RESTful API。

缓存

我读到的第一件事说是使用cookie,并在服务器端(散列)和客户端(未散列)存储会话ID /登录令牌。客户端将使用每个请求传输此id,服务器将对其进行哈希处理,解析并相应地处理请求。这不是RESTful(不是一个大问题),但更重要的是,我是否必须复制我的API:一个用于用户名/密码验证(例如通过curl完成)和一个用于基于cookie的验证(例如我的网络应用程序)?

另一个问题是:如果我有一个用户的多个连接,我会怎么做,例如他们登录了两个浏览器,iPhone和iPad。我的会话ID存储现在需要是一个数组吗?

HTTP Basic Auth

下一个想法是使用HTTP Basic Auth(使用SSL),这似乎很容易,但不推荐使用,因为您需要为每个请求传输用户名和密码。如果我使用HTTP Basic Auth进行,我是否会将用户名和密码存储在cookie(或HTML本地存储)中以允许“记住我”功能?或者我可以将两者结合起来:对实际请求使用HTTP Basic Auth(发布新帖子等),只使用存储在cookie中的会话ID进行初始登录顺序/记住我的方面吗?

传输会话ID比传输用户密码更安全吗?怎么样? 会话ID将表面上作为密码,因此对我来说,传输它会产生与传输密码相同的安全问题。

所有平台似乎都支持Basic Auth,这是理想的选择。主要缺点似乎是需要随每个请求传输客户端身份验证数据。有没有办法缓解这个问题?

的OAuth

OAuth对我的需求似乎有些过分。我想我将无法使用curl命令来测试我的API。 OAuth如何改进cookies方法?

正如你可能会说的那样,我对可用的各种信息感到有些困惑,所以如果你有一套适用于这种情况的良好链接,我很乐意阅读它们。我正在尝试找到适合所有平台的解决方案,但仍然尽可能安全。另外,如果我的任何术语有误,请纠正我,因为它会让我更容易搜索。

感谢。

更新

我一直在考虑这个问题,我有一个想法。如果这是愚蠢/不安全/任何反馈,请告诉我,因为我不确定它是否好。

当用户登录时,我们会生成随机会话ID(salted等)。此可选会话ID将发送到客户端,客户端可以选择存储(例如,在cookie中);会话ID存储在数据库中。

此会话ID随后可选地与每个请求一起作为HTTP身份验证标头或查询字符串发送,或者客户端可以根据需要发送用户名和密码(这为我们提供常规REST) API)。在服务器端,我们首先检查会话ID参数,如果它不存在,我们检查用户名/密码。如果两者都没有 - 错误。

在服务器上,我们检查会话ID是否与正确的用户名相关联。如果是,我们完成请求。

每次用户登录时,我们都会创建一个新的会话ID或删除当前的会话ID,并将其与请求中的日志响应一起发送。

我认为这可以让我在适当的情况下使用常规REST API和Basic Auth,并维护会话/记住我的功能。它没有解决多个登录问题,但我认为应该这样做。请告诉我。

3 个答案:

答案 0 :(得分:26)

我会使用基于令牌的身份验证,您可以在每次请求时自动发送令牌。您必须登录一次,服务器将为您提供一个令牌,然后您可以使用该令牌随每个请求发送。此标记将添加到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/)等库交换。例如,Amplify使用任何可用的本地存储。这将转化为这样的事情:

    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/

答案 1 :(得分:1)

请看yeoman发生器的角度和节点? generator-angular-fullstack具有非常好的结构,可以使用护照进行用户身份验证。

你可以在这里看到一个例子:

代码:https://github.com/DaftMonk/fullstack-demo

结果:http://fullstack-demo.herokuapp.com/

希望它有所帮助!

答案 2 :(得分:0)

我使用generator-angular-fullstack,/ api服务不安全,从/ api / users / me获取你的_id,注销,然后转到/ api / users / your_id_here,你会发现/ api不安全。