用于在 OpenFire 服务器中创建 XMPP 用户的类似适配器的模式和代码
用于通过 HTTP POST 从最终用户应用程序创建 OpenFire XMPP 用户的 PHP 代码和模式。
引言
本文提供了一种类似适配器的模式,以及可用于
- 使用 HTTP POST 创建 OpenFire XMPP 用户。
- 将对一组允许的 OpenFire REST API 操作的访问权限限制给经过认证的客户端。
- 验证已创建用户的邮箱。
- 将用户的 XMPP 密码设置为足够随机的值,或重置密码。
- 仅允许访问此 REST API 子集操作的客户端应用程序访问,这些应用程序拥有客户端面向令牌。
本文介绍的模式解决了从授权的 XMPP 应用程序内部自动创建用户的问题。替代方法,例如带内注册的 XEP-0077,容易受到垃圾邮件发送者和无法控制的新用户注册的影响。基于 Web 的替代方法,例如用于通过 Web 注册页面创建用户的 OpenFire 注册插件,可能不适合所有应用程序。
- 它们不支持自动注册,没有最终用户干预,并且,
- 注册不能限制为授权应用程序。
稍后描述的代码通过分发客户端面向令牌来限制对一组选定的允许的 OpenFire REST 操作的访问。由于这使拥有令牌访问权限的任何人都可以创建用户,因此分发时必须小心。
此处提供的 PHP 代码是可配置的,并且已在 Azure 网站应用程序中进行了测试。所呈现的架构模式可应用于任何具有基于 HTTP 的管理界面的 XMPP 服务器,用于可控的新用户注册。
背景
XMPP 是 Extensible Messaging and Presence Protocol 的简写,它是一种基于 XML 的面向消息的中间件的开放标准通信协议。XMPP 广泛用于即时消息应用程序中的人与人通信,但也可用于物联网应用程序以及机器到机器的消息、状态信息等的分布。
Openfire XMPP 服务器提供用于管理 Openfire XMPP 服务器的 REST API 。此 API 提供用于添加、删除、修改用户、组的方法,能够 完全管理 Openfire 服务器。但是,授予对该 API 的访问权限意味着访问该服务的服务将获得对您的 Openfire 服务器的管理级别访问权限。如果这是一个受信任的、与公众隔离的服务,那么这是可以接受的。
因此,不得从要分发到互联网的应用程序或从任何客户端面向的应用程序直接使用此 REST API。例如,必须不惜一切代价避免允许从应用程序商店分发的移动应用程序访问 REST 服务。如果连接令牌被发现、逆向工程或泄露,潜在的攻击者将获得完全管理 XMPP Openfire 服务器的访问权限。
因此,需要一个面向 HTTP 客户端的适配器服务,以便
- 限制最终用户对 REST API 子集的访问。当前实现仅提供创建和密码重置操作,可供最终用户应用程序访问。
- 限制 REST API 服务授权令牌的分发。最终用户应用程序通过单独的客户端面向令牌访问服务。虽然这存在一些安全风险,但即使泄露,也可能仅提供创建新用户(使用已验证的电子邮件)和重置已知用户密码的访问权限。
- 验证用户邮箱。用户以临时随机密码创建,该密码通过电子邮件发送,从而确认邮箱。
以明文形式发送用户密码不是最佳选择,应仅被视为最终用户应用程序的一次性临时密码。最终用户应用程序应强制用户在使用此密码时更改。
通过向电子邮件发送一次性密码重置链接,而不是临时 XMPP 密码,可以使其更安全并在未来进行扩展。
在此解决方案中,配置和连接字符串变量从环境中读取。这些变量在 Azure PHP Web 应用的应用程序设置和连接字符串配置变量中设置。SendGrid 服务用于发送确认电子邮件。
下面是待描述模式的功能块架构草案。
依赖项
使用 Composer PHP 依赖管理器解析依赖项。适配器需要这些包:
- php-openfire-restapi,一个简单的 PHP 类,用于包装 Openfire Rest Api 插件功能。此类提供对 REST API 的完全访问权限,不适用于最终用户客户端应用程序。
- sendgrid PHP 库,用于使用 SendGrid 服务。
- paragonie/random_compat 库,用于生成足够安全的临时密码。
使用代码
要使用代码,只需
- 下载附加的 userMgtAdapter.zip 文件并解压。
- 将 userMgtAdapter.php 文件和 composer.json 文件放在您选择的 PHP 应用程序位置。
- 运行 Composer 以解析依赖项。
- 定义以下环境变量:
- APPSETTING_SHARED_SECURITY_TOKEN,客户端最终用户应用程序可以访问 userMgtAdapter 服务的安全令牌。
- APPSETTING_API_HOST,XMPP Openfire 服务器主机。
- APPSETTING_API_PORT,XMPP Openfire 服务器 REST 服务监听的端口。请注意,这仅通过 SSL。
- CUSTOMCONNSTR_API_SECRET,连接到 Openfire REST 服务的 API 密钥。
- APPSETTING_FROM_EMAIL_ADDR,用于发送验证消息的电子邮件。
- CUSTOMCONNSTR_EMAIL_API_SECRET,SendGrid API 密钥。
- APPSETTING_CREATE_EMAIL_SUBJECT, APPSETTING_RESET_EMAIL_TEXT, APPSETTING_RESET_EMAIL_HTML, APPSETTING_RESET_EMAIL_SUBJECT, APPSETTING_RESET_EMAIL_TEXT, APPSETTING_RESET_EMAIL_HTML 分别是创建用户和重置密码消息的主题、文本和 HTML 内容。用户姓名和密码使用 %s 表示法包含在内。
对于 Azure Web 应用,可以在 Azure 管理门户中定义环境变量,无需 APPSETTING 和 CUSTOMCONNSTR 前缀,因为 Azure 的 Web 应用的应用程序设置和连接字符串变量。
请注意,必须在 PHP 配置文件中设置 APPSETTING_API_HOST 的证书,以抑制 PHP curl 的任何错误消息。有关如何将证书添加到 PHP Azure 安装的信息,请参阅这篇文章。
现在您可以访问页面来创建用户、发送验证电子邮件和重置密码。要访问该服务,只需向 https://www.example.com/userMgtAdapter.php 发送一个 application/json 的 HTTP POST 调用,并附带此 json 主体:
{ "token": "YourClientFacingSecret", "action": "create", "username": "usersJidNode", "email":"usersemail@example.com", "name":"Joe Do" }
或
{ "token": "YourClientFacingSecret", "action": "reset_pass", "username": "usersJidNode", "email":"usersemail@example.com", "name":"Joe Do" }
reset_pass 操作还可以更新用户信息。成功执行后:
- 返回 HTTP 状态码 200。
- 将一封电子邮件发送给用户的邮箱,其中包含临时密码。最终用户应用程序必须强制更改此密码。
关注点
文章中使用了 SendGrid 电子邮件服务 API,使用了此函数:
/* Send email with the following self explaining parametersw */ function senduserEmail ($to,$emailWrapper) { if (empty($emailWrapper->ServiceApiSecret)) { returnErrorCode(503,"Email Service API Secret not initialized correctly"); printf("Email Service API Secret not initialized correctly emailServiceApiSecret = $emailWrapper->ServiceApiSecret"); printf(" userJidNode= $userJidNode userEmail = $email password = $password sharedToken = $sharedToken serviceAction = $serviceAction userName = $userName"); return; } $sendgrid = new SendGrid($emailWrapper->ServiceApiSecret); //Create a new SendGrid userEmail object and add your message details. $email = new SendGrid\Email(); $email ->addTo($to) ->setFrom($emailWrapper->from) ->setSubject($emailWrapper->subject) ->setText($emailWrapper->text) ->setHtml($emailWrapper->html) ; $sendgrid->send($email); }
如前所述,如果请求的操作成功且未发生异常,则返回 HTTP 状态码 200。如果未提供客户端面向或 REST API 面向的安全令牌,或者它们不正确,则会根据错误条件返回 4XX 或 5XX 错误,使用此函数:
/* Return an HTTP Error Code, indicating an operation failure */ function returnErrorCode ($httpStatusCode, $httpStatusMsg) { $phpSapiName = substr(php_sapi_Name(), 0, 3); if ($phpSapiName == 'cgi' || $phpSapiName == 'fpm') { header('Status: '.$httpStatusCode.' '.$httpStatusMsg); } else { $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0'; header($protocol.' '.$httpStatusCode.' '.$httpStatusMsg); } }
结论
本文介绍了一种类似适配器的模式,可以用于任何希望适配必须不能在互联网、第三方应用程序或最终用户客户端上共享的接口的服务。
本文以 PHP 提供了流行的 Openfire XMPP 服务器的示例,但相同的模式可以应用于任何提供管理 REST API 的 XMPP 服务器。
历史
本文的第一个版本。