hapi_使用Hapi进行OAuth集成

hapi

保护Web资源通常是一项艰巨而艰巨的任务。 如此之多,以至于它常常被留到开发的最后阶段,然后匆忙地完成了。 这是可以理解的。 安全是开发中一个非常专业的领域,大多数人只是想了一下:“是的,这应该是安全的……”因此,开发人员很快就采用了一种临时的安全方法:

if (password === "password1") {
  setCookie();
}
else {
  send(401);
}

并在产品出厂时充满安全漏洞。 希望该代码片段过分简化,但这一点仍然有效。

值得庆幸的是,那里开发人员花费大量时间尝试保护网站和Web资源的安全,我们可以依靠他们的专业知识来帮助我们保护自己的项目,而不必重新发明轮子。

在本文中,我们将逐步使用OAuth令牌通过其GitHub凭据对用户进行身份验证。 所有这些单词在一起听起来似乎非常困难,但是由于有一些文档齐全的模块,我想您会惊讶它的真正实现如此简单。

先决条件

假定读者:
1.具有使用hapi服务器框架的功能理解。
2.过去已经建立了Web资源。
3.对cookie有基本的了解。
4.有一个GitHub帐户。
5.对誓言的含义及其用途有基本的了解(您可以先阅读有关它的Wikipedia文章 )。

如果这些假设中的任何一个都不成立, 强烈建议您先处理列出的前提条件,然后再回来学习有关保护网页安全的知识。

入门

您需要做的第一件事是创建一个GitHub应用程序。 此过程将为您提供ClientIDClientSecret –这两个值都是在Web服务器上设置OAuth所需的。

  1. 登录您的GitHub帐户并转到设置页面(https://github.com/settings/profile)
  2. 点击“应用程序”
  3. 按下“ Generate new application”按钮,您将被导航到一个新屏幕,如下所示:
    注册新的OAuth应用程序
  4. 应用程序名称应用程序描述可以是任何您想要的。 对于Homepage URLAuthorization回调URL ,我们将它们设置为将要使用的本地服务器。 在我的示例中,我将使用端口9001,因此将两个值都设置为“ http:// localhost:9001”。 我的完整设置如下所示:
    完成的应用程序配置
  5. 按下“注册应用程序”后,您将被重定向到一个新屏幕,其中将列出ClientIDClientSecret 。 记下这些值以备后用。

摘要

此步骤纯属行政管理。 我们创建了一个新的GitHub应用程序,当用户尝试登录到您的网站时,系统将询问该用户。 我们不会信任GitHub凭据来信任http:// localhost:9001,而是会信任GitHub应用程序来对用户进行身份验证,然后在完成后回叫我们的网站。

规划服务器

在开始编码之前,让我们大致概述一下我们希望服务器执行的操作。 为了简单起见,我们将从四种路由开始:家庭路由,帐户信息路由,登录路由和注销路由。

在家庭路由中,如果用户已通过身份验证,请打印其名称,否则打印一条通用消息。 对于帐户路由,我们将显示GitHub发送给我们的所有信息。 如果用户未经过身份验证就请求帐户页面,我们将以正确的状态码401进行响应。登录路由将到达GitHub,请该用户授予其许可以允许我们的GitHub应用程序访问其一些帐户信息,然后返回到我们的本地Web服务器。 最后,登出路线将使用户退出我们的网站。

服务器骨架

让我们首先获得样板并路由配置。

var Hapi = require('hapi');
var server = new Hapi.Server();

server.connection({ port: 9001 });

server.register([], function (err) {

    if (err) {
        console.error(err);
        return process.exit(1);
    }

    server.route([{
            method: 'GET',
            path: '/login',
            config: {
                handler: function (request, reply) {

                    // Reach out to GitHub, ask the user for permission for their information
                    // if granted, response with their name
                    reply();
                }
            }
        }, {
            method: 'GET',
            path: '/account',
            config: {
                handler: function (request, reply) {

                    // Show the account information if the have logged in already
                    // otherwise, send a 491
                    reply();
                }
            }
        }, {
            method: 'GET',
            path: '/',
            config: {
                handler: function (request, reply) {

                    // If the user is authenticated reply with their user name
                    // otherwise, replay back with a generic message.
                    reply();
                }
            }
        }, {
            method: 'GET',
            path: '/logout',
            config: {
                handler: function (request, reply) {

                    // Clear the session information
                    reply.redirect();
                }
            }
        }
    ]);
    server.start(function (err) {

        if (err) {
            console.error(err);
            return process.exit(1);
        }

       console.log('Server started at %s', server.info.uri);
    });
});

清单1最基本的hapi服务器

摘要

上面的代码创建了一个服务器,并在9001端口上建立了连接,并添加了一些带有处理程序功能存根的路由。 您会注意到server.register([], function() {...} ,我们正在传递一个空数组,继续,我们将开始向hapi中添加插件,但是对于最初的样板,我们将其保留我们正在使用server.route来指定我们要构建的四个路由,并将它们传递给pathmethod字符串以及一个config对象,该config对象将在下一部分中大量使用。每条路由都带有空响应。如果启动服务器,应该看到:

Server started at http://hostname.local:9001

您应该能够对所有已定义的路由进行GET请求,并收到200个空响应。

如果您过去使用过hapi,那么此样板中的内容都不会让人感到惊讶。 如果没有,请转到此处的文档站点以帮助清除问题。

插入

hapi最好的部分之一是插件系统。 插件允许将hapi应用程序的各个段细分为小型便携式模块。 您可以使用hapi服务器对象执行几乎所有操作,也可以使用插件进行操作。 您可以添加路由,扩展点,侦听事件,创建缓存段; 甚至注册主服务器对象中唯一的视图引擎。 有关插件的更多信息,请查看hapijs.com上的教程

对于此示例,我们将使用bellhapi-auth-cookie插件。

bell是一个hapi插件,旨在处理与第三方OAuth提供程序集成所需的大部分繁琐的握手。 它内置了对最常用的OAuth客户端(Facebook,Twitter,GitHub和Google等)的支持。 这意味着,OAuth与GitHub集成的大部分繁重工作已经完成。 我们只需要配置我们的hapi服务器即可使用它。

bell处理OAuth所需的所有来回操作, 在成功验证用户身份后才调用关联的hapi处理函数。 否则,hapi将以401响应。需要特别注意的一件事是bell没有任何用户会话概念。 意味着一旦单个请求已通过第三方认证,该认证将对后续请求丢失。 您可以使用bell来保护所有路由,但是用户对您的网站提出的每个单个请求都将需要OAuth跳舞,这会非常低效。 我们需要一种创建包含OAuth会话信息的安全cookie并使用该安全cookie认证将来的请求的方法。

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

hapi-auth-cookie提供了易于使用的cookie会话管理。 用户必须通过其他方式进行身份验证; hapi-auth-cookie所做的只是提供一个API来获取和设置加密的cookie。 它具有其他一些实用程序功能,但重要的是要了解它自己不会进行任何身份验证。

hapi-auth-cookie通过通过request.auth.session添加方法来扩展hapi request对象; 特别是request.auth.session.setrequest.auth.session.clearset用于创建安全会话cookie并clear以将其删除。 这些方法被添加到“ onPreAuth” 服务器扩展点内

对于我们的服务器,bell将负责所有OAuth协商,一旦成功,请使用hapi-auth-cookie通过request.auth.session.set设置加密的cookie。

配置插件

在下一个代码部分中,我们将填充空register功能,并为从图1开始的服务器配置两个插件。

var Hapi = require('hapi');
var Bell = require('bell');
var AuthCookie = require('hapi-auth-cookie');

//... refer to Listing 1

server.register([Bell, AuthCookie], function (err) {

    if (err) {
        console.error(err);
        return process.exit(1);
    }

    var authCookieOptions = {
        password: 'cookie-encryption-password', //Password used for encryption
        cookie: 'sitepoint-auth', // Name of cookie to set
        isSecure: false
    };

    server.auth.strategy('site-point-cookie', 'cookie', authCookieOptions);

    var bellAuthOptions = {
        provider: 'github',
        password: 'github-encryption-password', //Password used for encryption
        clientId: 'huU4KjEpMK4TECW',//'YourAppId',
        clientSecret: 'aPywVjShm4aWub7eQ3ub3FbADvTvz9',//'YourAppSecret',
        isSecure: false
    };

    server.auth.strategy('github-oauth', 'bell', bellAuthOptions);

    server.auth.default('site-point-cookie');

    //... refer to Listing 1

清单2配置bell和hapi-auth-cookie插件

代码说明

server.register是将插件添加到hapi服务器的入口点。 它支持几种不同的函数签名,但是出于我们的需要,我们将传递一个对象数组。 每个对象必须实现一个register函数,该函数将被调用并提供当前的hapi服务器对象。 一旦所有插件都已注册,便会执行回调。

我们需要在这里稍作绕道,以说明hapi如何处理身份验证。 使用hapi进行身份验证可分为两个概念: 模式和策略。 该文档在这里对其进行了最佳描述:

将方案视为一般身份验证类型,例如“基本”或“摘要”。 另一方面,策略是方案的预配置和命名实例。

除了非常具体和高级的情况以外,您将使用预建方案并配置适合您的应用程序的特定策略。 身份验证策略将在整个应用程序中使用以保护资源,这是方案的“实例”。 方案是认证请求的一种手段。 bell和hapi-auth-cookie都通过server.auth.scheme注册新方案; “响铃”和“ cookie”方案。

方案名称是server.auth.strategy的第二个参数。 在注册使用该方案的策略之前,必须先将该方案注册到hapi服务器。 这就是为什么我们需要首先注册插件,然后通过server.auth.strategy设置策略。

在清单2中,我们首先注册一个“ cookie”策略,并将其命名为“ site-point-cookie”。 在整个代码中,我们将引用“ site-point-cookie”来引用此已配置的cookie策略。 在此处可以找到所有可用选项的完整说明。 在我们的示例中,我们仅使用passwordcookieisSecurepassword应该是一个很强的字符串,因为Iron模块将使用它来加密和解密cookie。 cookiecookie的名称, isSecure设置结果Set-Cookie标头的'Secure'选项。 这意味着该cookie仅通过HTTPS连接传输。 我们现在将其设置为false ,以使使用本示例更加容易,但通常应将其设置为true

github-oauth

第二种更有趣的策略是名为“ github-oauth”的“钟”类型。 与“ site-point-cookie”注册类似,我们传递名称,方案和选项对象。 响铃策略选项的完整列表可以在此处的响铃仓库中找到provider设置为“ github”,因为bell内置了对GitHub OAuth集成的支持。 如果您尝试与未知的提供程序集成,也可以将其设置为对象。 password是在协议授权步骤期间用于加密临时 cookie的字符串。 该cookie仅在授权步骤期间持续存在,之后被销毁。 clientIdclientSecret是我们在“入门”部分中创建的值。 清单2中的值不会工作,因为他们是这个例子只是随机胡言乱语,你将需要插入你自己的价值观到代码。 最后, isSecure与“ site-point-cookie”具有相同的功能。

最后,我们为整个服务器设置默认身份验证,以使用名为“ site-point-cookie”的cookie策略。 这只是一个方便的设置。 它告诉hapi对添加到server.route每个路由使用“ site-point-cookie”策略对请求进行身份验证。 这大大减少了每条路由所需的重复配置选项的数量。

使它工作

我们终于完成了所有的配置和设置! 剩下的只是几行逻辑,将所有内容连接在一起。 一旦看到所需的代码量,您就会看到hapi实际上是一个以配置为中心的框架。 让我们遍历清单1中的每条路线,并将配置对象和处理程序更新为起作用。

登录路线

登录路由是需要联系GitHub服务器并进行OAuth跳舞的路由。 清单3显示了更新的route config选项:

method: 'GET',
path: '/login',
config: {
    auth: 'github-oauth',
    handler: function (request, reply) {

        if (request.auth.isAuthenticated) {

            request.auth.session.set(request.auth.credentials);
            return reply('Hello ' + request.auth.credentials.profile.displayName);
        }

        reply('Not logged in...').code(401);
    }
}

清单3登录路由更新

此处仅config选项已更改。 首先,我们要将auth选项设置为'github-oauth'。 此值引用了清单2中创建的名为“ github-oauth”的“响铃”策略。 这告诉hapi在尝试验证此路由时使用'github-oauth'策略。 如果我们忽略此选项,则hapi将回退并使用清单2中指定的默认策略。 'site-point-cookie'。 可用的auth选项的完整列表不在本文讨论范围之内,但是您可以在此处阅读有关它们的更多信息。

在处理程序函数中,我们检查request的request.auth.isAuthenticated值。 request.auth添加到在启用了身份验证的路由上进行request 。 如果isAuthenticated为true,我们想设置一个cookie来表明这一点。 记住,hapi-auth-cookie使用setclear函数向request.auth添加了一个session对象。 因此,既然用户已经通过GitHub进行了身份验证,我们希望创建一个会话cookie,以将其与request.auth.session.set一起在整个应用程序中使用,并传递从GitHub返回给我们的凭据对象。 根据我们传递给hapi-auth-cookie的选项,这将创建一个名为“ sitepoint-auth”的加密cookie。 最后,我们想回应一则消息,显示GitHub显示名称。

如果用户未通过身份验证或拒绝GitHub OAuth访问,我们将以一条消息和401状态代码进行响应。

帐户路径

如果用户已登录,则帐户路由应显示用户GitHub信息,如果未登录,则响应401。更新的配置和处理程序代码如下清单4所示。

method: 'GET',
path: '/account',
config: {
    handler: function (request, reply) {

        reply(request.auth.credentials.profile);
    }
}

清单4帐户路由更新

这条路线没有太多变化。 因为我们没有覆盖config对象中的任何auth值,所以此路由使用默认的cookie策略。 当请求帐户路由时,hapi将查找“ sitepoint-auth” Cookie,并确保它存在并且对于该请求是有效的Cookie。 如果是,将调用处理程序,否则响应将为request.auth.credentials是我们在清单3的登录路线中设置的cookie值,而profile是GitHub存储大多数用户帐户信息的位置。

在这一点上,您应该能够测试我们添加的两条路由(“ / login”和“ / account”),并查看它们如何协同工作以及如何响应。

本国路线

像大多数网站一样,我们应该在网站的根部有一条路由。 回顾我们希望该路由执行的操作,应根据用户身份验证状态来定制响应。 如果用户未登录,则不应收到401,而是应该看到非定制的主页。 如果他们已登录,我们希望以自定义的消息欢迎他们回来。

method: 'GET',
path: '/',
config: {
    auth: {
        mode: 'optional'
    },
    handler: function (request, reply) {

        if (request.auth.isAuthenticated) {
            return reply('welcome back ' + request.auth.credentials.profile.displayName);
        }

        reply('hello stranger!');
    }
}

清单5本地路由更新

清单5为auth设置引入了一个新概念; modemode值可以采用三个字符串值之一; “必需”,“可选”和“尝试”。 “必需”表示请求必须具有当前有效的身份验证。 “可选”意味着请求不需要有身份验证,但如果这样做,它必须是有效的。 最后,“尝试”与“可选”相同,但是身份验证不必有效。

此路由具有清单2中设置的默认cookie策略,因此我们所需要做的就是设置mode ,该strategy将为“ site-point-cookie”。 在处理程序中,我们可以像清单3一样检查请求的auth状态。如果为true,则用户具有有效的“ sitepoint-auth” cookie,我们可以通过响应请求来响应request.auth.credentials存储的信息request.auth.credentials ; 就像清单4一样。如果auth状态为false,那么我们对用户一无所知,处理程序函数将以一条通用消息进行回复。 尝试将mode更改为“必需”,然后清除cookie以查看“必需”和“可选”之间的区别。

登出路线

最后,让我们更新注销路由以删除会话cookie。

method: 'GET',
path: '/logout',
config: {
    auth: false,
    handler: function (request, reply) {

        request.auth.session.clear();
        reply.redirect('/');
    }
}

清单6注销路由更新

因为我们对所有路由都有默认的身份验证策略,所以我们想对此路由禁用auth以允许任何请求通过。 记住如果使用默认策略,这将很有用。 否则,您将最终对服务器的每个请求进行身份验证,并且您可能不希望这样做。 特别是对于静态资源。 在处理程序中,我们调用request.auth.session.clear()来取消设置“ sitepoint-auth” cookie,最后将用户重定向回站点的根目录。 如果用户没有“ sitepoint-auth” Cookie,则此代码本质上是“无操作”,但不会造成任何伤害,并且完全安全。

摘要

这似乎是很多话,但是其中大部分是在解释配置选项以及一些hapi身份验证内部工作原理。 hapi将身份验证分为两个概念; 计划和策略。 方案是身份验证的一般类型,策略是方案的配置实例。 我们使用bell与GitHub进行OAuth跳舞,并使用hapi-auth-cookie将用户的GitHub信息保存到名为“ sitepoint-auth”的加密cookie中。 我们在整个应用程序的其余部分中都使用了此cookie,以确定身份验证状态。

实际的路由处理程序中的大多数代码极其琐碎,因为繁重的工作大部分是由hapi插件完成的。 在登录路径中,我们设置了一个安全cookie,其中包含从GitHub发送的所有信息。 在帐户资源中,cookie的当前内容作为JSON发送回用户。 在本地路由中,我们更改了身份验证mode以允许no auth和auth的混合使用,这是根资源的非常常见的情况,并做出了相应的响应。 最后,我们完全禁用了注销路由的身份验证,并清除了“ sitepoint-auth” Cookie,并将用户重定向到主页。

希望在阅读本文之后,您将看到所需的大部分工作只是在配置中。 除了基本的hapi样板之外,几乎没有代码。 我鼓励您在此处查看完整的工作代码并使用不同的选项和身份验证设置自行进行试验。


如果您想了解有关Hapi.js的更多信息,请观看我们的带有Hapi.js构建插件迷你课程的示例视频。 在本课程中,您将通过一系列视频介绍Hapi的基础知识,这些视频包括路由,视图,请求生命周期以及Hapi强大的插件系统。

正在加载播放器…

翻译自: https://www.sitepoint.com/oauth-integration-using-hapi/

hapi

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值