65.9K
CodeProject 正在变化。 阅读更多。
Home

使用蓝牙耳机适配器保护 Web 应用程序

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2013 年 5 月 7 日

CPOL

6分钟阅读

viewsIcon

19828

在本文中,我将向您展示一个概念验证代码片段,演示如何使用蓝牙耳机适配器的序列号来帮助为 Web 应用程序提供身份验证机制。

引言

人们越来越普遍地认为,仅靠密码不足以保护应用程序。需要“双因素身份验证”来更充分地保护敏感数据。双因素身份验证是指除了密码之外,还需要提供某种物理标识符,以帮助向系统识别用户。双因素身份验证的典型实现方式是使用物理令牌或手机应用程序,该应用程序每 60 秒生成一个唯一的序列号。

幸运的是,Plantronics 新推出的 Legend UC 耳机适配器附带一个用于识别它的唯一序列号。有了这些额外信息,我们就可以将其用作此身份验证机制的一部分。在本文中,我将向您展示一个概念验证代码片段,演示如何使用蓝牙耳机适配器的序列号来帮助为 Web 应用程序提供身份验证机制。这不是一个完全安全的实现,并且有一些方法可以增强该实现,但我会将这些练习留给安全专家。

项目设置

在此示例中,我将从一个 ASP.Net Empty Web Application 开始。为了使用 Plantronics Spokes 客户端库,我们需要向应用程序添加 jQuery。我可以通过在程序包管理器控制台中运行以下命令轻松添加 jQuery

Install-Package jQuery

安装 jQuery 后,我还必须添加对 _spokes.js_ 文件的引用。您可以在 Plantronics SDK 的“Zeus”示例中获取此脚本的副本。如果您已安装 SDK,_spokes.js_ 文件将位于:_C:\Program Files (x86)\Plantronics\Plantronics SDK\Samples\Zeus_

我在 _default.html_ 文件中组装了一个非常简单的登录框,如下所示

这个简单的框允许用户输入他们的 _userid_、_password_ 和当前的 authenticator 值。在我的假设场景中,我们将自动填写 authenticator 框,其中包含耳机中的序列号值。此框的 HTML 如下所示

  <fieldset id="loginBox">

    <legend>Login:</legend>

    <form method="post" action="/account/login.aspx">
      <b>User Id:</b><input type="text" id="userId" name="userId" />
      <br />
      <b>Password:</b><input type="password" name="pass" />
      <br />
      <b>Authenticator:</b><input type="text" id="authenticator" name="authenticator" />
      <br />
      <input type="submit" value="Login" name="text" id="btnLogin" />
    </form>
 
  </fieldset>

激活表单

要激活与 Plantronics 适配器的交互,我们需要开始添加一些 JavaScript 引用,并构建一个与耳机服务通信的脚本。首先,我将在 HTML 文件中添加对 _spokes.js_ 和 _jquery.js_ 文件的脚本引用。接下来,我将创建一个名为 _auth.js_ 的新脚本文件并添加对其的引用,该文件将包含我执行身份验证的所有逻辑。

专业提示: 在任何 JavaScript 文件的顶部创建一个引用,以允许 Visual Studio 2010 和 2012 的 intellisense 识别您正在处理的脚本内容,并在您编码时提供有用的工具提示。只需在文件顶部创建一个格式为

/// <reference path="spokes.js" />

然后 Visual Studio 会将这些引用提供给您。

对于此表单,我将编写一个完整的 JavaScript 对象来维护所有信息。我将从一个自执行的匿名函数结构开始,如下所示

/// <reference path="spokes.js" />
var auth = (function () {

})();

这将定义一个新对象,请记住函数在 JavaScript 中是对象,并将其分配给全局作用域的 auth 变量。在函数对象内部,我将定义一个构造函数,该构造函数将开始轮询适配器以获取有关设备和状态更改的信息。

var auth = (function () {

  var pollingInterval = 5000;

  var Auth = (function () {

    var spokes = new Spokes("http://127.0.0.1:32001/Spokes");
    var attached = false;

    // Constructor
    function Auth() {
      ConnectSpokes();
      setInterval(function () {
 
        ConnectSpokes();

      }, pollingInterval);
 

    }

    Auth.prototype.devicePresent = false;
    Auth.prototype.serialNumber = "NOT SET";

    return Auth;
 
  })();

  return new Auth();

})();

这里有几件事正在发生:首先,声明了一个私有作用域变量 _pollingInterval_,并将其设置为 5000ms 的间隔。接下来,创建了一个内部对象,其中包含用于 _Spokes_ 对象和用于记住我们是否已成功连接到设备的私有作用域变量。接下来,定义了一个构造函数,它将尝试使用 _Spokes_(即将出现的函数)连接到适配器,然后设置一个周期性轮询来维护该连接。

定期轮询 _Spokes_ 连接状态非常重要,因为 _Spokes_ JavaScript 库不会通知我们何时断开与设备的连接。相反,该库会将事件发布到我们必须轮询的内部队列中。无法查看适配器的当前状态,您必须记住最后一个触发的事件是什么,并据此行事。通过采取这些步骤,我们可以根据设备状态的变化自动格式化和重新格式化登录框。

执行此轮询的 _ConnectSpokes_ 函数利用了 _Spokes_ Device 对象的调用。这是该函数的来源

function ConnectSpokes () {
      spokes.Device.deviceList(function (result) {
        if (result.isError) {
          console.log("Unable to connect to headset");
          SetLoginBox(false);
        }
        else if (result.Result[0] == null) {
          console.log("Error - Is there a headset connected?");
          SetLoginBox(false);
        }
        else {
          console.log("Connected to the headset adapter");
          Auth.prototype.devicePresent = true;
          Auth.prototype.serialNumber = result.Result[0].SerialNumber;
 
          SetLoginBox(true)
 
        }
 
      });
    };

_Spokes_ 库公开了一个 _deviceList_ 方法,该方法接受一个回调函数来处理对硬件的请求结果。此回调是异步执行的,因此 _ConnectSpokes_ 函数实际上会非常快速地返回控件,但直到稍后才完成执行。传递给此方法的结果对象被检查以确定连接到适配器时是否存在任何错误,并在控制台中记录适当的消息。如果需要,您可以轻松地更改此设置以执行其他操作;在此示例中,我调用 _setLoginBox_ 并传递一个 false 参数。如果我们成功连接到设备,我会适当地记录它,并使用 _Auth.prototype_ 语法在 _Auth_ 对象上设置两个静态方法。这两个变量表示 _devicePresent_ 和设备的序列号。最后,如果我们成功连接到耳机适配器,我会调用 _SetLoginBox_ 并传递一个 true 参数。

_SetLoginBox_ 函数是一个内部函数,它使用一些 jQuery DOM 操作来配置 authenticator 框,并拦截登录按钮的点击操作,以便从耳机适配器上的序列号设置 authenticator 值

function SetLoginBox(headsetPresent) {
      if ($("#loginBox")[0]) {
 
        if (headsetPresent) {
 
          if (!attached) {
            $("#authenticator").attr("disabled", true).css({ backgroundColor: "silver", color: "red", fontWeight: "bold", textAlign: "center" }).val("HEADSET");
            $("#btnLogin").click(function () {
              $("FORM").append("<input type='hidden' value='" + auth.serialNumber + "' name='authenticator'/>");
            })
            attached = true
          }
 
        } else {
          if (attached) {
            $("#authenticator").removeAttr("disabled").css({ backgroundColor: "transparent", color: "inherit", fontWeight: "inherit", textAlign: "inherit" }).val("");
            $("#btnLogin").unbind("click");
            attached = false;
          }
        }
 
      }
    };

我将此方法的执行包装在一个对 _loginBox_ 对象的测试中。通过采取这种防御性编码措施,我可以将此代码添加到应用程序中 _loginBox_ 可能出现的任何页面上。最后,我需要将 HTML 对象的检查和格式化包装在一个对类作用域的附加变量的检查中。没有这个检查,此方法会将多个单击事件处理程序附加到 _btnLogin_ 对象。如果此方法执行了数百次,并且将数百个处理程序挂接到登录按钮的单击事件上,这将导致严重的内存泄漏。

当耳机适配器存在时,此方法的最终格式如图所示

这是一个简单的实现,但概念是合理的:您可以使用蓝牙设备提供身份验证功能。

下载本文的源代码Plantronics SDK 进行试用。我相信您会发现一些很棒的功能可以在您的应用程序中使用

© . All rights reserved.