只用JavaScript驗證安全不安全
誰都知道,答案是不安全,非常的不安全。因為在客戶端進行的驗證相當於“讓用戶自己驗證自己”,很明顯是不靠譜的。你不能避免一些惡意用戶人為的修改自己的表單進行欺騙,也不能避免第三方對表單進行截獲后進行篡改再提交。
所以說,從安全的角度來說,單純的依靠js驗證,是不安全的,任何健壯的系統都必須在后端進行驗證。
雙驗證大大增加了工作量,如何解決?
方案1:笨方法,都寫一遍
方案2:現有框架 ,比如MVC自帶驗證支持雙向驗證 ,不足點是要寫 model加attrbute 也要有一定工作量
方案3:自已封裝
我的選擇方案:方案3
思路
page 加載時通過Key去存儲表 form規則,通過form規則生成前台元素的綁定,完成前台驗證。后台函數通過key在獲取表單規則進行后台驗證。(可以用緩存機質提高性能)
實現
后台代碼:
通過GetInitScript存儲form規則並且賦值給 ViewState["intisript"]去前台綁定

前台調用只要綁定 viewState["intiscript"] (其實什么都不要寫,保證元素name和 viewstate中一致就可以了):
<body>
<form id="form1" runat="server" class="contact_form">
<ul>
<li>
<h2>
表單驗證</h2>
<span class="required_notification">* 表示必填項</span> </li>
<li>
<label for="name">
姓名:</label>
<input type="text" name="name" />
</li>
<li>
<label>
姓別:</label>
<input type="radio" value="1" name="sex" />男
<input type="radio" value="0" name="sex" />女 </li>
<li>
<label for="email">
電子郵件:</label>
<input type="email" name="email" />
</li>
<li>
<label for="website">
手 機:</label>
<input type="text" name="phone" />
</li>
<li>
<label for="website">
學 歷:</label>
<select name="education" >
<option value="">==請選擇==</option>
<option value="1">大學</option>
</select>
</li>
<li>
<label for="message">
備注:</label>
<textarea name="remark" cols="40" rows="6"></textarea>
</li>
<li></li>
</ul>
<br />
<asp:Button ID="Button1" runat="server" Text="submit" CssClass="submit" OnClick="Button1_Click" />
</form>
<%=ViewState["intiscript"]%>
</body>
ViewState["intiscript"] 將生成一段腳本 給HTML元素添加 pattern、placeholder和requierd 等屬性 ,有了這些屬性可以很方便的使用JS等插件進行前端驗證
下面是通過ViewState["intiscript"] 生成出來的HTML

后台使用 PostValidation函數進行驗證

我們來看看效果:


提交成功驗證通過了,下面我來改下前端元素采 用惡意參數 提交后台

前台驗證通過:

后台還是要把你給揪出來

最后附上C#驗證類代碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace SyntacticSugar
{
/// <summary>
/// ** 描述:可以方便實現前后端雙驗證,基於jquery
/// ** 創始時間:2015-6-4
/// ** 修改時間:-
/// ** 作者:sunkaixuan
/// ** 使用說明:http://www.cnblogs.com/sunkaixuan/p/4550580.html
/// </summary>
public class ValidationSugar
{
private static List<ValidationOption> ValidationOptionList = new List<ValidationOption>();
/// <summary>
/// 前台注入
/// </summary>
/// <param name="pageKey"></param>
/// <param name="itemList"></param>
public static string GetInitScript(string pageKey, List<OptionItem> itemList)
{
//初始化后不在賦值
if (ValidationOptionList.Any(it => it.PageKey == pageKey))
{
return (ValidationOptionList.Single(c => c.PageKey == pageKey).Script);
}
else
{
ValidationOption option = new ValidationOption();
string uk = Guid.NewGuid().ToString().Replace("-", "");//唯一函數名
string script = @"<script>
var bindValidation{1}=function(name,params){{
var selectorObj=$(""[name='""+name+""']"");
selectorObj.after(""<span class=\""form_hint\"">""+params.tip+""</span>"");
if(params.pattern!=null)
selectorObj.attr(""pattern"",params.pattern);
if(params.placeholder!=null)
selectorObj.attr(""placeholder"",params.placeholder);
if(params.isRequired==true)
selectorObj.attr(""required"",params.isRequired);
}}
{0}</script>";
StringBuilder itemsCode = new StringBuilder();
foreach (var item in itemList)
{
switch (item.Type)
{
case OptionItemType.Mail:
item.Pattern = @"^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$";
break;
case OptionItemType.Int:
item.Pattern = @"^\\d{1,11}$";
break;
case OptionItemType.Double:
item.Pattern = @"^\\d{1,11}$";
break;
case OptionItemType.IdCard:
item.Pattern = @"^(\\d{15}$|^\\d{18}$|^\\d{17}(\\d|X|x))$";
break;
case OptionItemType.Date:
item.Pattern = @"^(((1[8-9]\\d{2})|([2-9]\\d{3}))([-\\/])(10|12|0?[13578])([-\\/])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\\d{2})|([2-9]\\d{3}))([-\\/])(11|0?[469])([-\\/])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\\d{2})|([2-9]\\d{3}))([-\\/])(0?2)([-\\/])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\\/])(0?2)([-\\/])(29)$)|(^([3579][26]00)([-\\/])(0?2)([-\\/])(29)$)|(^([1][89][0][48])([-\\/])(0?2)([-\\/])(29)$)|(^([2-9][0-9][0][48])([-\\/])(0?2)([-\\/])(29)$)|(^([1][89][2468][048])([-\\/])(0?2)([-\\/])(29)$)|(^([2-9][0-9][2468][048])([-\\/])(0?2)([-\\/])(29)$)|(^([1][89][13579][26])([-\\/])(0?2)([-\\/])(29)$)|(^([2-9][0-9][13579][26])([-\\/])(0?2)([-\\/])(29))|(((((0[13578])|([13578])|(1[02]))[\\-\\/\\s]?((0[1-9])|([1-9])|([1-2][0-9])|(3[01])))|((([469])|(11))[\\-\\/\\s]?((0[1-9])|([1-9])|([1-2][0-9])|(30)))|((02|2)[\\-\\/\\s]?((0[1-9])|([1-9])|([1-2][0-9]))))[\\-\\/\\s]?\\d{4})(\\s(((0[1-9])|([1-9])|(1[0-2]))\\:([0-5][0-9])((\\s)|(\\:([0-5][0-9])\\s))([AM|PM|am|pm]{2,2})))?$";
break;
case OptionItemType.Mobile:
item.Pattern = @"^[0-9]{11}$";
break;
case OptionItemType.Telephone:
item.Pattern = @"^(\\(\\d{3,4}\\)|\\d{3,4}-|\\s)?\\d{8}$";
break;
case OptionItemType.Fax:
item.Pattern = @"^[+]{0,1}(\\d){1,3}[ ]?([-]?((\\d)|[ ]){1,12})+$";
break;
case OptionItemType.Regex:
break;
}
itemsCode.AppendFormat("bindValidation{0}('{1}',{{ tip:'{2}',pattern:'{3}',placeholder:'{4}',isRequired:{5} }})", uk, item.FormFiledName, item.Tip, item.Pattern, item.Placeholder,item.IsRequired?"true":"false");
itemsCode.AppendLine();
}
option.Script = string.Format(script, itemsCode.ToString(), uk);
script = null;
itemsCode.Clear();
option.PageKey = pageKey;
option.ItemList = itemList;
ValidationOptionList.Add(option);
return (option.Script);
}
}
/// <summary>
/// 后台驗證
/// </summary>
/// <param name="pageKey"></param>
/// <param name="errorMessage">json格式</param>
/// <returns></returns>
public static bool PostValidation(string pageKey, out string errorMessage)
{
bool isSuccess = true;
errorMessage = string.Empty;
if (!ValidationOptionList.Any(c => c.PageKey == pageKey))
{
throw new ArgumentNullException("ValidationSugar.PostValidation.pageKey");
}
var context = System.Web.HttpContext.Current;
var itemList = ValidationOptionList.Where(c => c.PageKey == pageKey).Single().ItemList;
var successItemList = itemList.Where(it => (it.IsRequired && !string.IsNullOrEmpty(context.Request[it.FormFiledName]) || !it.IsRequired)).Where(it => Regex.IsMatch(context.Request[it.FormFiledName], it.Pattern.Replace(@"\\",@"\"))).ToList();
isSuccess = (successItemList.Count == itemList.Count);
if (!isSuccess)
{
errorMessage = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(itemList);
}
return isSuccess;
}
private class ValidationOption
{
public string PageKey { get; set; }
public string Script { get; set; }
public List<OptionItem> ItemList { get; set; }
}
public enum OptionItemType
{
Mail = 0,
Int = 2,
Double = 3,
IdCard = 4,
Date = 5,
/// <summary>
/// 移動電話
/// </summary>
Mobile = 6,
/// <summary>
/// 座機
/// </summary>
Telephone = 7,
Fax = 8,
/// <summary>
/// 沒有合適的,請使用正則驗證
/// </summary>
Regex = 1000
}
/// <summary>
/// 驗證選項
/// </summary>
public class OptionItem
{
/// <summary>
/// 驗證類型
/// </summary>
public OptionItemType Type { get; set; }
/// <summary>
/// 正則
/// </summary>
public string Pattern { get; set; }
/// <summary>
/// 是否必填
/// </summary>
public bool IsRequired { get; set; }
/// <summary>
/// 表單字段名(name或者id)
/// </summary>
public string FormFiledName { get; set; }
/// <summary>
/// 水印
/// </summary>
public string Placeholder { get; set; }
/// <summary>
/// 提醒
/// </summary>
public string Tip { get; set; }
}
}
}
源碼下載:http://pan.baidu.com/s/1mgoXpsW
時間問題只支持HTML5驗證,需要高版本瀏覽器,以后我會慢慢完善