Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/vba/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Node.js和Redis中实现基于令牌的授权API?_Node.js_Api_Authentication - Fatal编程技术网

如何在Node.js和Redis中实现基于令牌的授权API?

如何在Node.js和Redis中实现基于令牌的授权API?,node.js,api,authentication,Node.js,Api,Authentication,我在一个web应用程序中工作,它处理来自Mongo数据库的资源,对于这些资源,我想提供一个API,以便未来的移动应用程序可以从原始客户端获取或使用它 然而,我希望web应用程序使用相同的API,在这里我对如何正确实现这一点感到困惑 以下是我迄今为止所做的工作: API认证: 所以基本上我接收消费者的用户名和密码,根据数据库对其进行身份验证,如果匹配,我回复一个令牌(实际上是一个UUID)。该令牌与数据库中的用户id成对存储在Redis中。将来对任何API路由的每个请求都将验证这种令牌是否存在 我

我在一个web应用程序中工作,它处理来自Mongo数据库的资源,对于这些资源,我想提供一个API,以便未来的移动应用程序可以从原始客户端获取或使用它

然而,我希望web应用程序使用相同的API,在这里我对如何正确实现这一点感到困惑

以下是我迄今为止所做的工作:

API认证: 所以基本上我接收消费者的用户名和密码,根据数据库对其进行身份验证,如果匹配,我回复一个令牌(实际上是一个
UUID
)。该令牌与数据库中的用户id成对存储在Redis中。将来对任何API路由的每个请求都将验证这种令牌是否存在

我想知道:

  • 我应该如何管理令牌TTL,以及在将来请求时续订?
  • 如何控制每次请求的时间窗口限制?
  • 我采取的方法中是否有任何安全警告?
网站授权: 基本上,我对数据库执行相同的用户名密码身份验证,然后:

1。启动新的服务器会话。

2。当然,提供一个带有会话ID的cookie。

3。然后我创建redisuuid和用户ID记录,API将对其进行检查。我想这没关系,因为再次请求
POST/api/auth
身份验证是有意义的。

我想知道:

  • 这是最好的方法吗?
  • 我是否应该包含任何标记盐来区分纯API消费请求和web应用请求?
  • 我采取的方法中是否有任何安全警告?
  • 我应该包括更多代币吗?
这是
POST/login
的示例:

app.route('/login')

    .post(function (request,response,next) {

        var email = request.body.email;
        var password = request.body.password;

        var login = new Account({"local.email":email,"local.password":password});

        Account.findOne({"local.email":email}, function (err,user) {

            if (err) {
                                response.redirect('/error');
            }

            if (!user) {

                                var cookie = request.cookies.userAttempts;

                                if (cookie === undefined) {

                                    response.cookie('userAttempts',1);
                                }

                                else {

                                    response.cookie('userAttempts',(++cookie));
                                }

                                response.redirect('/');
            }

            else {

                user.validPassword(password, function (err,matched) {

                    if (err) {

                                    // Redirect error site or show err message.
                                    response.redirect('/error');
                    }

                    if (matched) {

                                    var session = request.session;
                                    session.userid = user._id;
                                    var uuidToken = uuid.v4();
                                    redisClient.set(uuidToken,user._id,redis.print);
                                    redisClient.expire(uuidToken,900);                                        
                                    response.cookie('email',email);
                                    response.redirect('/start');
                    }

                    else {

                            var cookie = request.cookies.passwordAttemps;

                            if (cookie === undefined)

                                response.cookie('passwordAttemps',1);

                            else {
                                var attemps = ++request.cookies.attemps
                                response.cookie('passwordAttemps', attemps)
                            }

                            response.redirect('/');                                    
                    }

                });                    
            }


        });
    })

我想我可以不用使用和编写典型的会话实现,而以某种方式依赖API所具有的类似基于令牌的身份验证。

您所拥有的一切都是正确的,基本上取代了Cookie的一些功能。有一些事情要考虑,你已经接触了其中一些。

  • 虽然使用UUID(我猜是v4?)很好,因为它是不确定的和“随机的”,但就其本身而言,令牌是毫无价值的。如果redis丢失数据,则令牌不再具有任何上下文。如果没有redis的帮助,您也无法强制执行到期。与之相比,它可以单独携带上下文,任何人都可以使用正确的密钥解密,可以处理过期,并且可以强制执行进一步的公共应用程序级别约束(颁发者、受众等)

  • 速率限制。除了您可能会使用令牌作为在速率限制器中跨请求识别用户的密钥这一事实之外,它们很少与您选择的令牌方案直接相关

  • 在web应用程序和其他客户端(移动应用程序、桌面应用程序等)上透明地传递令牌可能是一个巨大的痛苦。为了访问私有资源,用户需要在请求中的某个位置传递令牌,可能是头,对于web应用程序,这意味着您需要手动干预,将令牌包含在每个请求中。这意味着对所有经过身份验证的请求手动编写ajax请求。虽然这可能很烦人,但至少这是可能的,如果你正在编写一个单页应用程序,你可能无论如何都会这么做。这同样适用于任何移动或桌面客户端。既然您已经必须直接在代码中发出HTTP请求,那么这又有什么关系呢?现在想象一下这样一个场景:返回html页面的HTTP GET端点只能通过正确的身份验证进行访问。对于web应用程序,用户很可能会通过浏览器重定向或直接在URL栏中键入来访问它。除了使用cookie(因为移动和桌面客户端没有实现cookie,所以您明确不使用cookie),这实际上是不可能的。但是,如果您的API客户机始终可以修改HTTP请求结构,那么这并不是一个真正的问题

  • 现在是一个无耻的插头。它主要在内部使用,因此对于它的依赖关系(express、redis)非常固执己见,但希望它能在这里帮助您。事实上,该库基本上只是一个JWT包装器,围绕着您现有的内容。如果您决定使用它并注意到任何问题或不足,请随时在github上提交任何问题。另外,npm上还有很多其他基于JWT的会话管理模块,看起来很有希望。不管怎样,我都会检查这些模块,因为很可能有比我们更好的模块。同样,我们的是在内部使用的,来自一组非常特定的用例,因此它捕获您所有用例的可能性非常小。另一方面,这听起来像是你在使用一个类似的堆栈,所以可能鞋适合


    如果您确实使用了我们的API,那么该模块的API界面上出现了一个分裂,您可以选择直接将数据存储在JWT声明或redis中,这似乎有些奇怪。这是经过深思熟虑的,我认为您的示例为双方都说明了一个很好的用例。通常我们要做的是将用户的电子邮件和姓名存储在JWT声明中,然后将更多动态会话数据存储在他们会话的redis中。例如,在登录时,您可以将发行人、受众和用户的电子邮件添加到JWT声明中,但不要添加任何与“userAttempts”相关的内容。然后,在尝试失败后,您将添加或修改与该JWT相关的redis中存储的会话数据上的“userAttempts”。一旦设置了JWT,就不可能在不生成新JWT的情况下修改其内容,因此请注意,如果您决定在JWT中保留相对动态的数据,服务器和客户端之间将不断交换新旧JWT。

    非常感谢您提出的扩展问题。我想我会考虑使用JWT。到目前为止,网站需要进行身份验证
    app.route('/login')
    
        .post(function (request,response,next) {
    
            var email = request.body.email;
            var password = request.body.password;
    
            var login = new Account({"local.email":email,"local.password":password});
    
            Account.findOne({"local.email":email}, function (err,user) {
    
                if (err) {
                                    response.redirect('/error');
                }
    
                if (!user) {
    
                                    var cookie = request.cookies.userAttempts;
    
                                    if (cookie === undefined) {
    
                                        response.cookie('userAttempts',1);
                                    }
    
                                    else {
    
                                        response.cookie('userAttempts',(++cookie));
                                    }
    
                                    response.redirect('/');
                }
    
                else {
    
                    user.validPassword(password, function (err,matched) {
    
                        if (err) {
    
                                        // Redirect error site or show err message.
                                        response.redirect('/error');
                        }
    
                        if (matched) {
    
                                        var session = request.session;
                                        session.userid = user._id;
                                        var uuidToken = uuid.v4();
                                        redisClient.set(uuidToken,user._id,redis.print);
                                        redisClient.expire(uuidToken,900);                                        
                                        response.cookie('email',email);
                                        response.redirect('/start');
                        }
    
                        else {
    
                                var cookie = request.cookies.passwordAttemps;
    
                                if (cookie === undefined)
    
                                    response.cookie('passwordAttemps',1);
    
                                else {
                                    var attemps = ++request.cookies.attemps
                                    response.cookie('passwordAttemps', attemps)
                                }
    
                                response.redirect('/');                                    
                        }
    
                    });                    
                }
    
    
            });
        })