nodejs / ExpressJs和Angular(单页面应用程序)中基于令牌的授权

时间:2015-06-07 20:12:17

标签: javascript angularjs node.js mongodb http-token-authentication

在我的应用程序中,在注册用户时,我在MONGO DB中保存用户名,密码和jwt生成令牌以及这些字段。当用户尝试使用正确的凭据登录时,我将发送带有存储令牌的响应。然后在客户端(在我的控制器中)我使用localstorage来存储令牌,这样我就可以为客户端发送的每个请求发送相同的令牌。但是我发现了一些有关此过程的问题:

  1. 我每次为一个用户生成相同的令牌。如果任何第三人能够获得令牌,那么他就可以访问受限制的页面。
  2. 通过将生成的令牌存储在MONGODB
  3. 中,我在db中浪费了空间
  4. 任何人都可以访问存储在除用户之外的localstorage中的令牌。
  5. 对于我的单页应用程序中的每个请求,我再次查询mongodb以获取该用户的令牌并进行验证。这里,我正在检查客户端和服务器端。
  6. 我正在使用jwt在我的应用程序中生成令牌,Node,Express,Mongoose

    我是否遵循了良好的程序。如果没有,请您为我的方法或任何新方法提供解决方案。 我搜索了许多网站基于令牌的授权和基于会话的授权,但没有任何对我有用。 注意:我是Nodejs的初学者,AngularjS

3 个答案:

答案 0 :(得分:0)

您不希望将JWT存储在mongoose中,因为它在登录时出现在标题中。您首先生成一个令牌,然后使用像crypto这样的模块对其进行哈希。

有不同的方法可以执行此操作,它们都使用Passport来处理令牌。这是一个示例项目Satellizer

我建议您生成angular-fullstack项目。然后浏览server / auth文件夹和客户端/帐户文件夹。您将了解如何在基于MEAN的应用程序中安全地处理身份验证。

答案 1 :(得分:0)

您应该将令牌存储在高级键值缓存工具中,例如:Redis 这将显着提高性能。

您将第一次从数据库获取令牌,然后将其存储在Redis中。我曾经将token设置为key,将username设置为value。下一个请求,将从缓存中提供令牌。使用Redis,您可以为令牌设置过期。

答案 2 :(得分:0)

当用户注册时,您需要生成类似于您现在正在执行的JWT。没关系。但是,您不需要将其保存到数据库中。你没有问,但我认为密码不应该以明文形式存储。在将其保存到数据库之前,您可以使用bcrypt进行加密。

当用户尝试使用正确的凭据登录时,我将发送带有存储令牌的响应

是的,这是正确的方法。

然后在客户端(在我的控制器中)我使用localstorage存储令牌,以便我可以为客户端发送的每个请求发送相同的令牌。

是的,在客户端,您可以将JWT保存到本地存储,并在后续请求中将其发送到服务器。

现在你的要点:

  1. 因此,您每次都不会拥有相同的JWT,您可以添加" exp"在有效载荷中声明(我假设您正在使用像jwt-simple这样的东西来生成JWT)。类似的东西:

    var payload = { sub: account.username, exp: moment().add(10, 'days').unix() }; var token = jwt.encode(payload, "secret");

  2. 您不需要将JWT存储在数据库中。在某些情况下,令牌颁发者(授权服务器)与资源服务器不同。资源服务器仅在请求中接收JWT,但资源服务器无法触及授权服务器使用的数据库。旁注:如果您最终需要支持刷新令牌,即您交给客户端的JWT最终需要到期,那么您可以将刷新令牌存储在数据库中。刷新令牌与JWT(访问令牌)不同。支持刷新令牌的复杂性将增加。

  3. 本地存储不是存储密码的地方,但可用于存储JWT。出于这个原因,JWT必须并且应该在一定时间后过期。

  4. 不确定是什么意思,说你检查客户端和服务器端。当客户端需要访问资源时(同样,公平地认为资源服务器可能与授权服务器不同),资源服务器唯一传递的是JWT。任何人都可以解码JWT。例如,尝试将JWT粘贴到此网站http://jwt.io/上。这就是JWT不应包含任何敏感数据的原因。但是,如果资源服务器知道授权服务器在对JWT进行编码时使用的秘密,则资源服务器可以验证签名。回到第三个项目符号,这就是为什么将JWT存储在客户端的本地存储中的原因。

  5. 更新我更新了此内容,以便在评论框中回答您的一些问题。

    用户点击'登录'按钮触发Angular控制器向服务器发送请求,如:

    $http.post(url, {
        username: $scope.username,
        password: $scope.password
    }).success(function(res) { ... })
    

    服务器收到POST请求,检查用户名/密码,然后生成JWT,然后发送回浏览器。请注意,它不必将JWT保存到数据库。代码类似于

    var payload = {
        sub: account.username,
        exp: moment().add(10, 'days').unix()
    };
    var token = jwt.encode(payload, "secret");
    res.status(200).json({      
      token: token
    });
    

    回到客户端,在上面的success()回调中,现在可以将JWT保存在本地存储中:

    .success(function(res) { $window.localStorage.setItem('accessJWT', res.token) })
    

    用户现在已通过身份验证。现在,当用户想要访问受保护资源时,用户不必提供用户名/密码。通过可以从本地存储中检索的JWT,客户端现在可以使用承载方案将JWT放入请求的Authorization头中,并将请求发送到服务器。在代码中,它希望:

    headers.Authorization = 'Bearer ' + token;
    

    服务器接收请求。同样,接收此请求的服务器不必与生成上述JWT的服务器相同。这两台服务器可以分布在两个不同的大陆。即使您将JWT保存在上面,这对于无法访问存储JWT的数据库的服务器也没有任何好处。但是这个服务器可以从请求的头部中提取承载令牌,验证令牌并继续正常的任务。

    希望这会有所帮助。