使用 HTML5 和 WCF 的魔力实现唯一身份验证
使用 HTML5 和 WCF 的魔力实现唯一身份验证
目录
引言
这是我第一次接触 HTML5。有一天,我想做一个简单又好看的应用。我以前没有接触过 HTML5,但当我看到 HTML5 的新特性和标签时,我决定用核心 HTML 来制作一些新东西。使用 HTML5 的最大优势在于它轻量级、平台无关,并且非常适合 iPhone、Android 等移动应用(对于 Win 7 手机,它不支持,因为微软仍在试验 HTML5,尚未在手机上引入 HTML5)。关于我的想法就到这里,让我们继续讲应用。这个应用基于登录表单,但它不是一个简单的身份验证表单。
在本文中,我们将构建一个创新的登录表单,用户通过将一个“webid”文件拖放到登录表单中进行身份验证。该文件包含一张图片和一些关于持卡人的信息。它利用了 HTML5、WCF 和一项名为 WebId
的功能。
系统要求
运行此应用程序最基本的要求是支持 HTML5 的浏览器。
浏览器:Firefox 3.6.3、Google Chrome 5.0、Apple Safari 4.05、Opera 10.53
您可以学到什么
作为初学者,您可以学习以下功能
- HTML5、CSS3、WebKit
- 使用 JavaScript 进行 Ajax 调用 WCF 服务
- JavaScript 中的 JSON 操作。
炫酷的注册表单
我使用“炫酷”这个词是因为我将用简单的方式告诉您如何使表单看起来更具吸引力。
如图所示的注册表单,我将表单分为 3 个部分
- 用户详细信息
- 地址详细信息
- 图片上传
所有部分都很简单,除了图片上传部分,我没有提供上传按钮。那么我如何上传图片呢?所以我使用了 HTML5 的拖放功能,您只需将图片拖放到框中即可。但我在此处增加了一个限制。您不能拖放大小超过 10KB 的图片。由于这是一个简单的演示应用程序,我没有深入研究如何调整任何大小的图片,所以只是设置了限制。
正如您在注册表单的圆角框中看到的,这是用于 WebKit 的非常简单的 HTML5 CSS
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
-khtml-border-radius: 5px;
border-radius: 5px;
您可以用一行代码使用 webkit
为背景添加渐变效果。
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#526ACE), to(#526ACE));
您可以在附件中的我的代码中找到有关 CSS 的更多详细信息。
图片上传
我特别想详细介绍这项功能,因为它对于那些不想从桌面位置查找图片然后上传的懒人来说更有趣。我通过使用 HTML5 的拖放功能节省了 5-6 秒。
让我们来看看我是如何完成这项任务的。
<fieldset>
<legend>Image Load</legend>
<ol>
<li><legend>Drag and Drop your Image here<legend>
<div id="holder">
</div>
<div id="status">
</div>
<script>
var y = null;
var holder = document.getElementById('holder'),
state = document.getElementById('status');
if (typeof window.FileReader === 'undefined') {
state.className = 'fail';
} else {
}
holder.ondragover = function()
{ this.className = 'hover'; return false; };
holder.ondragend = function()
{ this.className = ''; return false; };
holder.ondrop = function(e) {
this.className = '';
e.preventDefault();
var size = e.dataTransfer.files[0].size;
if (size > 10000) {
alert("Your image size is greater than
10 kb please Shrink image size");
window.location.reload(true);
}
else {
var file = e.dataTransfer.files[0],
reader = new FileReader();
reader.onload = function(event) {
holder.style.background =
'url(' + event.target.result + ') no-repeat center';
y = 'url(' + event.target.result + ') no-repeat center';
};
reader.readAsDataURL(file);
//var x = document.getElementById(el);
state.className = 'success';
state.innerHTML = 'Image SuccessFully Uploaded';
return false;
}
};
</script></li>
</ol>
</fieldset>
虽然这段代码本身是清晰易懂的,但我想对拖放功能做一些说明。正如您所见,我在表单中使用了 div
作为拖放区域。
在 JavaScript 中,我们正在
- 使用
document.getElementByID
在 DOM 中搜索放置目标。 - 当触发“拖动覆盖”(当用户将元素拖动到另一个元素上时)事件时,它将触发 CSS 类。
- 绑定放置事件,并在其中获取有关放置内容的一些数据。
- 现在通过 reader 读取
stream
,它将生成event.target.result
,这将帮助我们以 base64 格式获取图片,我们进一步将其用作webid
。
有关新标签的更多详细信息,请参阅资源。
关于炫酷表单装饰和 JavaScript 就说到这里。现在我们来处理 Ajax 调用 WCF 服务。
Ajax 调用 WCF 服务
在描述 Ajax 调用片段之前,我想先介绍一下服务器端的 WCF 服务。该服务以 Post
方法的 REST URI 的形式暴露。我希望发送数据作为 post,因为数据会很大。我使用 JSON 作为响应格式,因为它易于在 Ajax 调用中使用。由于这是一个演示应用程序,我没有以专业的方式使用 WCF,只是以新手的方式使用。
简单的注册用户方法
[OperationContract]
[WebInvoke(Method = "*",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "SignUpUser")]
string SignUpUser(string name, string email,string language, string phoneno,
string gender, string country, string image);
SignUpUser
的实现非常直接。我只是制作了一个 webid
格式,就像 JSON 格式一样,然后将其发送到相应的邮件。为了生成 JSON 字符串
,我使用了内置的 .NET 序列化器。
JavaScriptSerializer oSerializer = new JavaScriptSerializer();
string sJSON = oSerializer.Serialize(jsonWebIdList);
Web ID 格式
我们应该创建一个文件,可以将其拖放到表单上。它将是一个扩展名为“webid
”的文本文件。其内容是一个 JSON 对象,包含我们需要的所有数据。文件的一部分,名为 userdata
,列出了 name
、age
等信息。请记住,您不应信任文件中的数据。它仅应作为登录屏幕上给用户的反馈。
{
"filetype": "webid",
"signed":1234567890,
"userdata": {
"id": 1,
"name":"XYZ",
"gender": "Male",
"birthdate":19610804,
"phone":"1234567890",
"country":"us",
"language":"en_US",
"image": "" // Base 64 Image format
},
"keys": {
“Null”
}
}
现在我从 JavaScript 调用注册服务,它看起来会是这样的
var baseUrl = "https://:54976/RestServiceImpl.svc/";
//Ajax request function for making ajax calling through other object
function AjaxRequest(baseurl, type, callbackResponse, parameterString) {
this.BaseURL = baseurl;
this.Type = type;
this.Callback = callbackResponse;
this.createXmlRequestObject();
this.ParemeterString = parameterString;
}
// Create XMLHTTP OBJECT
AjaxRequest.prototype.createXmlRequestObject = function() {
if (window.ActiveXObject) { // INTERNET EXPLORER
try {
this.xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
this.xmlHttp = false;
}
}
else { // OTHER BROWSERS
try {
this.xmlHttp = new XMLHttpRequest()
} catch (f) {
this.xmlHttp = false;
}
}
if (!this.xmlHttp) { // RETURN THE OBJECT OR DISPLAY ERROR
alert('there was an error creating the xmlhttp object');
} else {
//return this.xmlhttp;
}
}
AjaxRequest.prototype.MakeRequest = function() {
try {
// PROCEED ONLY IF OBJECT IS NOT BUSY
if (this.xmlHttp.readyState === 4 || this.xmlHttp.readyState === 0) {
// EXECUTE THE PAGE ON THE SERVER AND PASS QUERYSTRING
this.xmlHttp.open(this.Type, this.BaseURL, false);
var that = this;
// DEFINE METHOD TO HANDLE THE RESPONSE
this.xmlHttp.onreadystatechange = function() {
try {
// MOVE FORWARD IF TRANSACTION COMPLETE
alert(that.xmlHttp.readyState);
if (that.xmlHttp.readyState == 4) {
alert(that.xmlHttp.status);
// STATUS OF 200 INDICATES COMPLETED CORRECTLY
if (that.xmlHttp.status == 200) {
// WILL HOLD THE XML DOCUMENT
var xmldoc;
if (window.ActiveXObject) { // INTERNET EXPLORER
xmldoc = new ActiveXObject("Microsoft.XMLDOM");
xmldoc.async = "false";
that.Callback(that.xmlHttp.responseText);
}
else { // OTHER BROWSERS
//writeMessage("MakeRequest", that.xmlHttp.responseText);
that.Callback(that.xmlHttp.responseText);
}
}
}
}
catch (e)
{ alert(e) }
}
switch (this.Type) {
case "GET":
//this.xmlHttp.setRequestHeader("Content-type", "application/json");
// MAKE CALL
this.xmlHttp.send(this.BaseURL);
break;
case "POST":
this.xmlHttp.setRequestHeader("Content-type", "application/json");
this.xmlHttp.send(this.ParemeterString)
}
}
else {
// IF CONNECTION IS BUSY, WAIT AND RETRY
setTimeout('GetAllAppsService', 5000);
}
} catch (e) {
alert(e);
}
}
从上面的代码函数可以看出,AjaxRequest
创建了 XMLHttpRequest()
对象,该对象进一步调用 AjaxRequest.prototype.MakeRequest
方法。我以面向对象的方式使用了 JavaScript,以便可以轻松地在任何地方进行调用。您需要做的就是创建一个 AjaxRequest
对象并调用 MakeRequest
函数。有关如何使用 JavaScript 作为 OOPS 的更多详细信息,请参阅 此处的技巧。
您也可以从这篇文章 此处获得一些帮助。
现在就像这样调用 Ajax 请求到 WCF 服务
AuthenticateLogin.prototype.SendDetailsToServer = function(parameters, localId) {
var url = baseUrl + "SignUpUser";
var parameterString = "{";
for (var i = 0; i < parameters.length; i++) {
parameterString = parameterString + '"'
+ parameters[i][0] + '":"'
+ parameters[i][1] + '" ,';
}
parameterString = parameterString.slice(0, parameterString.length - 1);
//writeMessage("AddNewReminderToServer", "Local id : "+localId);
parameterString = parameterString + "}";
var ajaxRequestObject = new AjaxRequest(url, "POST", function(responseText) {
var jsonobj = eval('(' + responseText + ')');
var result = jsonobj.SignUpUserResult;
if (result == "Successful") {
alert("SuccessfullyMail sent and you will redirect to login Page");
window.location = "https://:54976/UI/latestLogin.htm";
}
else {
alert("Message sending Fail! Please try again");
window.location.reload(true);
}
// writeMessage("handleresponse", jsonstr);
// writeMessage(" -> local id :", ajaxRequestObject.TempItemID);
}, parameterString);
ajaxRequestObject.TempItemID = localId;
//writeMessage("AddNewReminderToServer", "Local id in ajax object : " +
//ajaxRequestObject.TempItemID);
ajaxRequestObject.MakeRequest();
}
我想重点说明的一点是 parameterString
。我将 Body
请求自定义为 JSON 格式,因为 Ajax 请求头是 JSON 格式的。所以它只接受 body 中的 JSON 字符串
。
this.xmlHttp.setRequestHeader("Content-type", "application/json");
this.xmlHttp.send(this.ParemeterString)
这里,函数 (responseText
) 用作回调函数,它将在 Ajax 请求调用完成响应后被调用。ResponseText
是当响应通过 readystate
4 和状态 200 从服务器返回时的结果状态。
现在调用
function getDataFromthroughClass() {
var objSync = new AuthenticateLogin();
//string name, string email, string phoneNo, string gender, string country)
var name = document.getElementById("name").value;
var email = document.getElementById("email").value;
var phone = document.getElementById("phone").value;
var language = document.getElementById("language").value;
var gender = document.getElementById("gender").value;
var country = document.getElementById("country").value;
objSync.SendDetailsToServer(new Array(
new Array("name", name),
new Array("email", email),
new Array("language", language),
new Array("phoneno", phone),
new Array("gender", gender),
new Array("country", country),
new Array("image", y)));
}
登录表单
这一部分非常有趣,是我从 mattiasdanielsson 那里学到的东西。他提供了一种使用 HTML5 的拖放功能来验证 web id 的好方法。
在登录表单中,当用户想要进行身份验证时,他会将一个文件(例如“xyz.webid”)拖放到表单中,然后 JavaScript 将其作为 JSON 读取和解析。使用 jQuery,用户的数据(name
、gender
等)会显示在拖放区域,为用户提供视觉反馈。如果文件解析没有错误,就会显示一个输入框,用户在此输入四位数的 PIN 码。然后 JavaScript 使用 PIN 码以及已拖放文件中的“auth
” string
来创建发送到服务器的密钥……在此演示应用程序中,我没有使用 PIN 身份验证。
var objData;
$(document).ready(function() {
var $droptarget = $('#idBox'), $idCardSrc = $('#idCardSrc'),
$idBoxBg = $('#idBoxBg'),
$pinBox = $('#pinBox'), $pinInput = $('input', $pinBox);
$droptarget.bind('dragenter', function(e) {
e.stopPropagation();
e.preventDefault();
$droptarget.addClass('drophover');
$idBoxBg.text('Drop it now');
return false;
});
$droptarget.bind('dragleave', function(e) {
e.stopPropagation();
e.preventDefault();
$droptarget.removeClass('drophover');
$idBoxBg.text('Drop ID file here');
return false;
});
$droptarget.bind('dragover', function(e) {
e.stopPropagation();
e.preventDefault();
});
document.getElementById('idBox').addEventListener('drop', function(e) {
e.stopPropagation();
e.preventDefault();
为了拖放 webid
,我们需要为所有四个事件添加事件处理程序,并对它们使用 stopPropagation
和 preventDefault
。否则,您的浏览器将只显示已拖放的文件,而永远不会触发 drop
事件。另请注意,jQuery 的 bind()
方法用于前三个处理程序,但不是第四个。由于 jQuery 不支持 Event.dataTransfer
对象,我们必须使用原生 JavaScript 来绑定放置事件。
结论
我希望您会喜欢这个由 HTML5 魔力带来的不同的身份验证登录窗口。正如我一开始所说,我们可以通过以下方法为非常安全的网站更合乎逻辑地增强此登录身份验证
- 您可以匹配 base 64 图片进行验证。
- PIN 可以被哈希到
webid
中,或者只是将 PIN 与webid
的附件一起放到邮件中。 - 加密
webid
(在此演示应用程序中,我没有使用加密。)
关于此应用程序就到这里。更多详情,请使用讨论区。
历史
- 2010 年 12 月 22 日:初始版本
- 2011 年 3 月 22 日:更新文章