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

JQuery Mobile 和 Stanford Crypto Library

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (3投票s)

2015年3月3日

CPOL

6分钟阅读

viewsIcon

12556

downloadIcon

125

使用 Stanford Crypto Library 在 LocalStorage 中存储和检索加密密码

引言

HTML5 Local Storage 提供了一种不安全的方式来在您的计算机上存储数据,因为数据是以纯文本形式存储的。本文旨在演示如何加密和解密 Web 应用程序的密码,并将它们存储和检索自 LocalStorage。

一些简单的假设:我将假设您了解 LocalStorage 和 JQuery Mobile 应用程序,因为这是这里使用的框架。此外,在此练习中,我使用了带有 Microsoft WebMatrix 的 Windows 模拟器。在 WebMatrix 中,您还可以使用 iPad 和 iPhone 模拟器。我还没有找到 Android 设备模拟器。此模拟器可以通过内置键盘进行输入,就像用户实际使用设备一样。这使得我的测试有点慢,因为 iPad 和 iPhone 模拟器允许我的键盘输入。图 3 描绘了这一点。

下载 Stanford_Encrypt.zip

背景

我想加密我存储在 LocalStorage Web 中的信息,这样它就不会轻易被识别,尤其是密码。使用 Stanford Crypto 库,这很容易完成。有关此库的更多详细信息,请参见此处。在本练习中,我们将开发简单的“登录”和“注册”屏幕。我将通过定义用户界面和使整个过程正常工作的代码来探讨这一点。我不会解释从 LocalStorage 保存和检索记录的细节,因为我已经在此处涵盖了大部分内容。因此,请参考该文章以获取有关 LocalStorage 的 CRUD 操作以及消息框和警报框配置的更多详细信息。

使用代码

我们仅使用两个屏幕来定义我们的用户界面,即“登录”和“注册”页面。

登录 - html 定义和读取 LocalStorage 中用户详细信息的代码。

下面的图 1 描绘了“登录”页面背后 HTML 的结果输出。“登录”页面要求用户输入电子邮件地址和密码。输入详细信息后,将从 LocalStorage 中读取这些信息以检查是否存在,如果不存在,则通知用户他/她未注册。成功后,用户将被带到启动板以访问其他 Web 应用程序模块。在用户可以登录之前,他/她应该先注册。

图 1

“登录”屏幕的 HTML 定义如下所示

<div id="pgSignIn" data-role="page">
<header id="pgSignInheader" data-role="header" data-position="fixed">
<h1>Stanford Encrypt > Welcome to Stanford Encrypt</h1>
</header>
<div id="pgSignIncontent" data-role="content">
<form action="#" method="post" id="pgSignInForm" name="pgSignInForm">
<div data-role="fieldcontain">
<label for="pgSignInEmail" id="lblpgSignInEmail">Email Address<span style='color:red;'>*</span></label>
<input type="email" name="pgSignInEmail" id="pgSignInEmail" placeholder="Enter email address here." autocomplete="off" data-clear-btn="true" title="Enter email address here." required></input>
</div>
<div data-role="fieldcontain">
<label for="pgSignInPassword" id="lblpgSignInPassword">Password<span style='color:red;'>*</span></label>
<input autocomplete="off" type="password" name="pgSignInPassword" id="pgSignInPassword" placeholder="Enter password here." data-clear-btn="true" title="Enter password here." required></input>
</div>
<div><button type="submit" id="pgSignInIn" class="ui-btn ui-corner-all ui-shadow ui-btn-b">Sign In</button>
</div>
<div><button id="pgAddUserUp" class="ui-btn ui-corner-all ui-shadow">Sign Up</button>
</div>
</form>
</div>
<footer id="pgSignInfooter" data-role="footer" data-position="fixed">
<h1>Powered by JQM.Show © Anele Mbanga 2015</h1>
</footer></div>

图 2

这描绘了当用户未注册应用程序时出现的错误消息。当用户注册时,Web 应用程序会验证他们的用户名(即电子邮件地址)、密码以及他们是否处于活动状态。只有经过身份验证的用户才能登录。

成功后,用户将被带到启动板以访问可用用户列表。请参见图 4。

为了使“登录”代码正常工作,需要进行一些检查,如下面的代码所示。代码已注释,以表明正在发生的事情。有关警报使用的更多详细信息,请参阅我关于使用 JQuery Mobile 的 CRUD 应用程序的帖子。

app.SignInUser = function(Email,Password){
// get users
$('#pgSignIn').data('success', 'false');
Email = Email.replace(/ /g,'-');
var usersObj = app.GetUser();
// get current user
var userRec = usersObj[Email];
//this user does not exist, tell user
switch (userRec) {
case undefined:
$('#pgSignIn').data('success', 'false');
Email = Email.replace(/-/g,' ');
$('#alertboxheader h1').text('User Error');
$('#alertboxtitle').text(Email);
$('#alertboxprompt').text('This user is NOT registered in this App!');
$('#alertboxok').data('topage', 'pgSignIn');
Email = Email.replace(/ /g,'-');
$('#alertboxok').data('id', Email);
$.mobile.changePage('#alertbox', {transition: 'pop'});
return;
break;
default:
// verify password and status of account
var pwd = userRec.Password;
// decript the password
pwd = sjcl.decrypt('MashJQMShow', pwd);
var atv = userRec.Active;
if (Password != pwd) {
$('#pgSignIn').data('success', 'false');
Email = Email.replace(/-/g,' ');
$('#alertboxheader h1').text('Password Error');
$('#alertboxtitle').text(Email);
$('#alertboxprompt').text('The password specified is incorrect!');
$('#alertboxok').data('topage', 'pgSignIn');
Email = Email.replace(/ /g,'-');
$('#alertboxok').data('id', Email);
$.mobile.changePage('#alertbox', {transition: 'pop'});
return;
} else {
$('#pgSignIn').data('success', 'true');
}
if (atv == false) {
$('#pgSignIn').data('success', 'false');
Email = Email.replace(/-/g,' ');
$('#alertboxheader h1').text('Account Error');
$('#alertboxtitle').text(Email);
$('#alertboxprompt').text('This account is no longer active. Contact your System Administrator!');
$('#alertboxok').data('topage', 'pgSignIn');
Email = Email.replace(/ /g,'-');
$('#alertboxok').data('id', Email);
$.mobile.changePage('#alertbox', {transition: 'pop'});
return;
} else {
$('#pgSignIn').data('success', 'true');
}
//find if status is successful or not
var succ = $('#pgSignIn').data('success');
if (succ == 'true'){
pgSignInClear();
// show the page to display after sign in
$.mobile.changePage('#pgMenu', {transition: 'slide'});
}
break;
}
};

从上面的代码中,解密已存储密码的关键在于以下代码片段。

// verify password and status of account
var pwd = userRec.Password;
// decript the password
pwd = sjcl.decrypt('MashJQMShow', pwd);
var atv = userRec.Active;
if (Password != pwd) {
$('#pgSignIn').data('success', 'false');
Email = Email.replace(/-/g,' ');
$('#alertboxheader h1').text('Password Error');
$('#alertboxtitle').text(Email);
$('#alertboxprompt').text('The password specified is incorrect!');
$('#alertboxok').data('topage', 'pgSignIn');
Email = Email.replace(/ /g,'-');
$('#alertboxok').data('id', Email);
$.mobile.changePage('#alertbox', {transition: 'pop'});
return;

用于加密和解密的密码是 MashJQMShow,但您也可以在那里使用自己的密码。用于验证的密码是实际存储的加密密码,这就是我们在此处解密它的原因。

注册 - html 定义和结果输出

要注册,用户需要指定他们的名字、姓氏、电子邮件地址和密码。目前,他们还必须指定他们的用户类型和活动状态。对于新用户,最后两个详细信息可以隐藏。输入所有信息后,用户详细信息将保存到 LocalStorage。下面的图 3 是注册屏幕。

<div id="pgAddUser" data-role="page">

<header id="pgAddUserheader" data-role="header" data-position="fixed">
<h1>Stanford Encrypt > Add User</h1>
<a data-role="button" id="pgAddUserBack" data-icon="arrow-l" class="ui-btn-left">Back</a>
</header>
<div id="pgAddUsercontent" data-role="content">
<form action="#" method="post" id="pgAddUserForm" name="pgAddUserForm">
<div data-role="fieldcontain">
<label for="pgAddUserFirstName" id="lblpgAddUserFirstName">First Name<span style='color:red;'>*</span></label>
<input type="text" name="pgAddUserFirstName" id="pgAddUserFirstName" placeholder="Enter first name here." autocomplete="off" data-clear-btn="true" title="Enter first name here." required></input>
</div>
<div data-role="fieldcontain">
<label for="pgAddUserLastName" id="lblpgAddUserLastName">Last Name<span style='color:red;'>*</span></label>
<input type="text" name="pgAddUserLastName" id="pgAddUserLastName" placeholder="Enter last name here." autocomplete="off" data-clear-btn="true" title="Enter last name here." required></input>
</div>
<div data-role="fieldcontain">
<label for="pgAddUserEmail" id="lblpgAddUserEmail">Email Address<span style='color:red;'>*</span></label>
<input type="email" name="pgAddUserEmail" id="pgAddUserEmail" placeholder="Enter email address here." autocomplete="off" data-clear-btn="true" title="Enter email address here." required></input>
</div>
<div data-role="fieldcontain">
<label for="pgAddUserPassword" id="lblpgAddUserPassword">Password<span style='color:red;'>*</span></label>
<input autocomplete="off" type="password" name="pgAddUserPassword" id="pgAddUserPassword" placeholder="Enter password here." data-clear-btn="true" title="Enter password here." required></input>
</div>
<div data-role="fieldcontain">
<label for="pgAddUserConfirmPassword" id="lblpgAddUserConfirmPassword">Confirm Password<span style='color:red;'>*</span></label>
<input onfocus="ValidatePassword(document.getElementById('pgAddUserPassword'), this);
" oninput="ValidatePassword(document.getElementById('pgAddUserPassword'), this);
" autocomplete="off" type="password" name="pgAddUserConfirmPassword" id="pgAddUserConfirmPassword" placeholder="Enter confirm password here." data-clear-btn="true" title="Enter confirm password here." required></input>
</div>
<div dir="ltr" data-role="fieldcontain">
<label for="pgAddUserUserType" id="lblpgAddUserUserType">User Type<span style='color:red;'>*</span></label>
<select name="pgAddUserUserType" id="pgAddUserUserType" data-native-menu="false" data-mini="true" data-inline="true" dir="ltr" class="required">
<option value="null" data-placeholder="true">Select User Type</option>
<option value="Administrator">Administrator</option>
<option value="User">User</option>
</select>
</div>
<div data-role="fieldcontain">
<input type="checkbox" name="pgAddUserActive" id="pgAddUserActive" autocomplete="off" value="Active"></input>
<label for="pgAddUserActive" id="lblpgAddUserActive">Active</label>
</div>
<div><button type="submit" id="pgAddUserSave" class="ui-btn ui-corner-all ui-shadow ui-btn-b">Save User</button>
</div>
</form>
</div>
</div>

当用户注册时,用户详细信息将存储在 LocalStorage 中。密码首先用一个密码加密,然后再存储。这是在读取表单控件内容的函数中完成的。

function pgAddUserGetRec(){
//define the new record
var UserRec
UserRec = {};
UserRec.FirstName = $('#pgAddUserFirstName').val();
UserRec.LastName = $('#pgAddUserLastName').val();
UserRec.Email = $('#pgAddUserEmail').val();
UserRec.Password = $('#pgAddUserPassword').val();
UserRec.Password = sjcl.encrypt('MashJQMShow', UserRec.Password);
UserRec.ConfirmPassword = $('#pgAddUserConfirmPassword').val();
UserRec.ConfirmPassword = sjcl.encrypt('MashJQMShow', UserRec.ConfirmPassword);
UserRec.UserType = $('#pgAddUserUserType').val();
UserRec.Active = $('#pgAddUserActive').prop('checked');
return UserRec;
}

注意:用于加密密码的密码应与用于解密已保存数据的密码相同。加密之上的那一行从表单中读取密码,然后对其进行加密。此函数的输出将传递给保存用户详细信息到 LocalStorage 的方法。下面的方法进一步说明了这一点。

$('#pgAddUserSave').on('click', function(e){
e.preventDefault();
e.stopImmediatePropagation();
// save the User
var UserRec;
//get form contents into an object
UserRec = pgAddUserGetRec();
//save object to localstorage
app.addUser(UserRec);
});

 

Windows Phone 模拟器输入

Windows Phone 模拟器允许使用模拟器键盘输入。虽然这是一个非常有趣的功能,但我发现它比使用键盘和鼠标输入有点繁琐,但很高兴看到我的原型 Web 应用程序也可以在此模拟器上运行。要使用此模拟器测试您的 Web 应用程序,请先安装 Microsoft WebMatrix,在“运行”菜单中,可以选择添加其他模拟器。

图 4

启动板

图 5

启动板被定义为一个使用 CSS 进行精美格式化的列表视图,定义如下。

<div id="pgMenu" data-role="page" data-theme="b" class="my-page">
<header id="pgMenuheader" data-role="header" data-position="fixed">
<h1>Stanford Encrypt</h1>
</header>
<div id="pgMenucontent" data-role="content">
<ul data-role="listview" data-inset="true" id="sbItems">
<li data-icon="false"><a data-transition="slide" id="sbUser" href="#pgUser"><h2>Users</h2><p>Maintain Users</p><img height="200" width="100%" src="apps80.png" alt="Users" class="ui-li-thumb"></img></a></li>
<li data-icon="false"><a data-transition="slide" id="sbLogOff" href="#pgSignIn"><h2>Log Off</h2><p>Exit Stanford Encrypt</p><img height="200" width="100%" src="poweroffw.png" alt="Log Off" class="ui-li-thumb"></img></a></li>
</ul>
</div>
</div>

用户选择“用户”后,将显示 Web 应用程序中捕获的所有可用用户列表。最终用户可以单击特定用户以打开该用户的详细信息。下面的图 6 描绘了可用用户列表。

图 6

这里的列表视图是在运行时从系统中可用用户列表中更新的。当应用程序首次启动时,列表为空,如下所示。

<div id="pgUser" data-role="page">
<header id="pgUserheader" data-role="header" data-position="fixed">
<h1>Stanford Encrypt > Users</h1>
<a data-role="button" id="pgUserBack" data-icon="arrow-l" class="ui-btn-left">Back</a>
<a data-role="button" id="pgUserNew" data-icon="plus" data-theme="b" class="ui-btn-right">New</a>
</header>
<div id="pgUsercontent" data-role="content">
<ul data-role="listview" data-inset="true" id="pgUserList" data-autodividers="true" data-filter="true" data-filter-placeholder="Search Users" data-filter-reveal="false">
<li data-role="list-divider">UserHdr</li>
<li id="noUser">You have no users</li>
</ul>
</div>
</div>

动态列表视图更新程序会在显示列表页面之前执行此代码来执行更新。

// display existing records in listview of Records listing.
//display records in listview during runtime.
app.displayUser = function(){
// get User records.
var UserObj = app.getUser();
// create an empty string to contain html
var html = '';
// make sure your iterators are properly scoped
var n;
// loop over records and create a new list item for each
//append the html to store the listitems.
for (n in UserObj){
//get the record details
var UserRec = UserObj[n];
//define a new line from what we have defined
var nItem = UserLi;
//update the href to the key
nItem = nItem.replace(/Z2/g,n);
//update the title to display, this might be multi fields
var nTitle = '';
nTitle += UserRec.FirstName;
nTitle += ', ';
nTitle += UserRec.LastName;
//replace the title;
nItem = nItem.replace(/Z1/g,nTitle);
//there is a description, update the list item
var nDescription = '';
nDescription += UserRec.UserType;
//replace the description;
nItem = nItem.replace(/DESCRIPTION/g,nDescription);
html += nItem;
}
//update the listview with the newly defined html structure.
$('#pgUserList').html(UserHdr + html).listview('refresh');
};

图 7 描绘了一个选定的用户。这次我决定旋转屏幕,以便能够显示编辑屏幕的完整标题。添加用户/注册屏幕使用相同的代码,但屏幕标题会根据用户从何处访问屏幕而改变。当选择注册时,屏幕标题将为“注册”,而当从用户列表进行选择时,屏幕标题将为“添加用户”。

图 7

首先

为了使加密和解密工作,Stanford 的 Javascript 库已作为我们 Web 应用程序定义的一部分包含在内。您将在附带的 HTML 文件中注意到这一行。

<script src="stanfordcryptolibrary.js" type="text/javascript"></script>

关注点

使用 Stanford Crypto Library 进行加密和解密是一次非常愉快的经历。这教会我们可以在 localstorage 中存储加密数据。我想,有了这个,我甚至可以解密实际的 LocalStorage 密钥,但我还没有尝试过。与我之前的帖子相比,这里使用了新的模拟器,并且由于我想要快速运行和测试它,因此使用模拟器键盘输入有点繁琐。

历史

我的文章更多地谈论 JQuery Mobile 和 CRUD Web 应用程序,如此处所示。

一篇关于为该应用程序添加安全功能的后续文章也在此进行了讨论。

本文旨在对存储在 Localstorage 中的密码进行 128 位加密和解密,以演示如何保护存储在 LocalStorage 中的信息。

© . All rights reserved.