2008年5月4日
摘要: 我的分页控件(未完,待续)——控件件介绍及思路
一、 工作的层次
UI层和逻辑层。
UI层:显示首页、末页、上一页、下一页、页号导航、文本框输入页号;共计多少条记录、多少页、当前页号等信息。
逻辑层:提供分页算法(SQL语句),根据分页控件的属性,在运行的时候生成分页用的SQL语句。
二、  ...
阅读全文
*--------------- 客户端表单通用验证checkForm(oForm) -----------------
* 本程序最初是由wanghr100(灰豆宝宝.net)的checkForm基础上进行修改的,增加了很多功能,如下:
* 1.对非ie的支持
* 2.增加了内置表达式和内置提示
* 3.增加了显示方式(弹出式和页面显示式)
* 4.增加了显示一条和显示全部
* 5.进行了封装(CLASS_CHECK)
* 6.支持外接函数或表达式(应用在密码一致)
* 7.简化了调用方式,所有操作只需要<script language='javascript' src='checkform.js'>,
然后在HTML里定义各标签验证格式
* 8.对IE增加了对键盘输入的限制(如:定义usage='int'时,输入框只能输入数字(非IE无效)
* 9.增加了对disabled的不验证
* 10.自定义报警方式(重写showMessageEx方法)
*-----------------------------申明信息-----------------------------
*
* 作者: ttyp
* 邮箱: ttyp@21cn.com
* 博客: http://www.cnblogs.com/ttyp/
* 声明: 对本程序可以任意复制传播,但请保留这些声明,对于内置的表达式有些没有做到很严格,如果你
有好的建议和意见,欢迎邮件和我联系或者上我的博客留言
* 简介:
本程序只需要对需要验证的标签设置三个属性:usage,exp,tip
usage : 内置格式或表达式或函数
exp : 正则表达式(注意如果指定了usage则忽略exp)
tip : 出错提示(如果是内置格式可以不要此属性,有缺省提示)
调用时只需要引用<script language='javascript' src='checkform.js'>,然后为每个标记
增加以上3个属性(不一定需要全部)
* 原作者: wanghr100(灰豆宝宝.net)
* email: wanghr100@126.com
*
*--------------- 客户端表单通用验证checkForm(oForm) -----------------
*/
////////////////////////////////////////////////////////////////////////////////
function CLASS_CHECK()
{
this.pass = true; //是否通过验证
this.showAll = true; //是否显示所有的验证错误
this.alert = false; //报警方式(默认alert报警)
this.message = ""; //错误内容
this.first = null; //在显示全部验证错误时的第一个错误控件(用于回到焦点)
//定义内置格式
var aUsage =
{
"int":"^([+-]?)\\d+$", //整数
"int+":"^([+]?)\\d+$", //正整数
"int-":"^-\\d+$", //负整数
"num":"^([+-]?)\\d*\\.?\\d+$", //数字
"num+":"^([+]?)\\d*\\.?\\d+$", //正数
"num-":"^-\\d*\\.?\\d+$", //负数
"float":"^([+-]?)\\d*\\.\\d+$", //浮点数
"float+":"^([+]?)\\d*\\.\\d+$", //正浮点数
"float-":"^-\\d*\\.\\d+$", //负浮点数
//邮件
"email":"^\\w+((-\\w+)|(\\.\\w+))*\\@[A-Za-z0-9]+((\\.|-)[A-Za-z0-9]+)*\\.[A-Za-z0-9]+$",
"color":"^#[a-fA-F0-9]{6}", //颜色
"url":"^http[s]?:\\/\\/([\\w-]+\\.)+[\\w-]+([\\w-./?%&=]*)?$", //联接
"chinese":"^[\\u4E00-\\u9FA5\\uF900-\\uFA2D]+$", //仅中文
"ascii":"^[\\x00-\\xFF]+$", //仅ACSII字符
"zipcode":"^\\d{6}$", //邮编
"mobile":"^0{0,1}13[0-9]{9}$", //手机
"ip4":"^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}.\\d{1,3}$", //ip地址
"notempty":"^\\S+$", //非空
"picture":"(.*)\\.(jpg|bmp|gif|ico|pcx|jpeg|tif|png|raw|tga)$", //图片
"rar":"(.*)\\.(rar|zip|7zip|tgz)$", //压缩文件
"date":"^\\d{4}(\\-|\\/|\.)\\d{1,2}\\1\\d{1,2}$" //日期
};
//缺省消息
var aMessage =
{
"int" :"请输入整数", //整数
"int+" :"请输入正整数", //正整数
"int-" :"请输入负整数", //负整数
"num" :"请输入数字", //数字
"num+" :"请输入正数", //正数
"num-" :"请输入负整数", //负数
"float" :"请输入浮点数", //浮点数
"float+":"请输入正浮点数", //正浮点数
"float-":"请输入负浮点数", //负浮点数
"email" :"请输入正确的邮箱地址", //邮件
"color" :"请输入正确的颜色", //颜色
"url" :"请输入正确的连接地址", //联接
"chinese":"请输入中文", //中文
"ascii" :"请输入ascii字符", //仅ACSII字符
"zipcode":"请输入正确的邮政编码", //邮编
"mobile":"请输入正确的手机号码", //手机
"ip4" :"请输入正确的IP地址", //ip地址
"notempty":"不能为空", //非空
"picture":"请选择图片", //图片
"rar" :"请输入压缩文件", //压缩文件
"date" :"请输入正确的日期" //日期
}
var me = this;
function checkForm(oForm)
{
me.pass = true;
me.message = "";
me.first = null;
var els = oForm.elements;
//遍历所有表元素
for(var i=0;i<els.length;i++)
{
//取得格式
var sUsage = els[i].getAttribute("Usage");
var sReg = "";
//如果设置Usage,则使用内置正则表达式,忽略Exp
if(typeof(sUsage)!="undefined"&&sUsage!=null)
{
//如果Usage在表达室里找到,则使用内置表达式,无则认为是表达式;表达式可以是函数;
if(aUsage[sUsage]!=null)
{
sReg = aUsage[sUsage];
}
else
{
try
{
if(eval(sUsage)==false)
{
me.pass = false;
if(me.first==null)
{
me.first = els[i];
}
addMessage(getMessage(els[i]));
if(me.showAll==false)
{
setFocus(els[i]);
break;
}
}
}
catch(e)
{
alert("表达式[" + sUsage +"]错误:" + e.description)
return false;
}
}
}
else
{
sReg = els[i].getAttribute("Exp");
}
if(typeof(sReg)!="undefined"&&sReg!=null)
{
//对于失效状态不验证
if(isDisabled(els[i])==true)
{
continue;
}
//取得表单的值,用通用取值函数
var sVal = getValue(els[i]);
//字符串->正则表达式,不区分大小写
var reg = new RegExp(sReg,"i");
if(!reg.test(sVal))
{
me.pass = false;
if(me.first==null)
{
me.first = els[i];
}
//alert(reg);
//验证不通过,弹出提示warning
var sTip = getMessage(els[i]);
if(sTip.length==0&&typeof(sUsage)!="undefined"&&sUsage!=null&&aMessage[sUsage]!=null)
{
sTip = aMessage[sUsage];
}
addMessage(sTip);
if(me.showAll==false)
{
//该表单元素取得焦点,用通用返回函数
setFocus(els[i]);
break;
}
}
}
}
if(me.pass==false)
{
showMessage();
if(me.first!=null&&me.showAll==true)
{
setFocus(me.first);
}
}
return me.pass;
}
/*
* 添加错误信息
*/
function addMessage(msg)
{
if(me.alert==true)
{
me.message += msg + "\n";
}
else
{
me.message += msg + "<br>";
}
}
/*
* 显示错误
*/
function getMessage(els)
{
var sTip = els.getAttribute("tip");
if(typeof(sTip)!="undefined"&&sTip!=null)
{
return sTip;
}
else
{
return "";
}
}
/*
* 显示错误
*/
function showMessage()
{
//外接显示错误函数
if(typeof(me.showMessageEx)=="function")
{
return me.showMessageEx(me.message);
}
if(me.alert==true)
{
alert(me.message);
}
else
{
var divTip;
divTip = document.getElementById("divErrorMessage");
try
{
if(typeof(divTip)=="undefined"||divTip==null)
{
divTip = document.createElement("div");
divTip.id = "divErrorMessage";
divTip.name = "divErrorMessage";
divTip.style.color = "red";
document.body.appendChild(divTip);
}
divTip.innerHTML = me.message;
}catch(e){}
}
}
/*
* 获得元素是否失效(失效的元素不做判断)
*/
function isDisabled(el)
{
//对于radio,checkbox元素,只要其中有一个非失效元素就验证
if(el.type=="radio"||el.type=="checkbox")
{
//取得第一个元素的name,搜索这个元素组
var tmpels = document.getElementsByName(el.name);
for(var i=0;i<tmpels.length;i++)
{
if(tmpels[i].disabled==false)
{
return false;
}
}
return true;
}
else
{
return el.disabled;
}
}
/*
* 取得对象的值(对于单选多选框把其选择的个数作为需要验证的值)
*/
function getValue(el)
{
//取得表单元素的类型
var sType = el.type;
switch(sType)
{
//文本输入框,直接取值el.value
case "text":
case "hidden":
case "password":
case "file":
case "textarea": return el.value;
//单多下拉菜单,遍历所有选项取得被选中的个数返回结果"0"表示选中一个,"00"表示选中两个
case "checkbox":
case "radio": return getRadioValue(el);
case "select-one":
case "select-multiple": return getSelectValue(el);
}
//取得radio,checkbox的选中数,用"0"来表示选中的个数,我们写正则的时候就可以通过0{1,}来表示选中个数
function getRadioValue(el)
{
var sValue = "";
//取得第一个元素的name,搜索这个元素组
var tmpels = document.getElementsByName(el.name);
for(var i=0;i<tmpels.length;i++)
{
if(tmpels[i].checked)
{
sValue += "0";
}
}
return sValue;
}
//取得select的选中数,用"0"来表示选中的个数,我们写正则的时候就可以通过0{1,}来表示选中个数
function getSelectValue(el)
{
var sValue = "";
for(var i=0;i<el.options.length;i++)
{
//单选下拉框提示选项设置为value=""
if(el.options[i].selected && el.options[i].value!="")
{
sValue += "0";
}
}
return sValue;
}
}
/*
* 对没有通过验证的元素设置焦点
*/
function setFocus(el)
{
//取得表单元素的类型
var sType = el.type;
switch(sType)
{
//文本输入框,光标定位在文本输入框的末尾
case "text":
case "hidden":
case "password":
case "file":
case "textarea":
try{el.focus();var rng = el.createTextRange(); rng.collapse(false); rng.select();}catch(e){};
break;
//单多选,第一选项非失效控件取得焦点
case "checkbox":
case "radio":
var els = document.getElementsByName(el.name);
for(var i=0;i<els.length;i++)
{
if(els[i].disabled == false)
{
els[i].focus();
break;
}
}
break;
case "select-one":
case "select-multiple":
el.focus();
break;
}
}
//自动绑定到所有form的onsubmit事件
if(window.attachEvent)
{
window.attachEvent("onload",function(){for(var
i=0;i<document.forms.length;i++){var theFrom = document.forms[i];
if(theFrom){theFrom.attachEvent("onsubmit",function(){return
checkForm(theFrom);});}}});
}
else
{
window.onsubmit = function(e){var theFrom = e.target;if(theFrom){return checkForm(theFrom);}}
}
this.keyCheck = function()
{
if(window.attachEvent)
{
window.attachEvent("onload",function(){for(var
i=0;i<document.forms.length;i++){var theFrom = document.forms[i];
if(theFrom){myKeyCheck(theFrom);}}});
}
else
{
//TOOD
}
function myKeyCheck(oForm)
{
var els = oForm.elements;
//遍历所有表元素
for(var i=0;i<els.length;i++)
{
//取得格式
var sUsage = els[i].getAttribute("Usage");
//如果设置Usage,则使用内置正则表达式,忽略Exp
if(typeof(sUsage)!="undefined"&&sUsage!=null)
{
switch(sUsage.toLowerCase ())
{
case "zipcode":
case "int":
els[i].onkeypress = function(){return
/\d/.test(String.fromCharCode(event.keyCode))||(this.value.indexOf('+')<0?String.fromCharCode(event.keyCode)=="+":false)||(this.value.indexOf('-')<0?String.fromCharCode(event.keyCode)=="-":false);}
els[i].onpaste = function(){return !clipboardData.getData('text').match(/\D/);}
els[i].ondragenter = function(){return false;}
els[i].style.imeMode= "disabled";
break;
case "mobile":
case "int+":
els[i].onkeypress = function(){return
/\d/.test(String.fromCharCode(event.keyCode))||(this.value.indexOf('+')<0?String.fromCharCode(event.keyCode)=="+":false);}
els[i].onpaste = function(){return !clipboardData.getData('text').match(/\D/);}
els[i].ondragenter = function(){return false;}
els[i].style.imeMode= "disabled";
break;
case "int-":
els[i].onkeypress = function(){return
/\d/.test(String.fromCharCode(event.keyCode))||(this.value.indexOf('-')<0?String.fromCharCode(event.keyCode)=="-":false);}
els[i].onpaste = function(){return !clipboardData.getData('text').match(/\D/);}
els[i].ondragenter = function(){return false;}
els[i].style.imeMode= "disabled";
break;
case "float":
case "num":
els[i].onkeypress = function(){return /[\+\-\.]|\d/.test(String.fromCharCode(event.keyCode));}
els[i].onpaste = function(){return !clipboardData.getData('text').match(/\D/);}
els[i].ondragenter = function(){return false;}
els[i].style.imeMode= "disabled";
break;
case "float+":
case "num+":
els[i].onkeypress = function(){return /[\+\.]|\d/.test(String.fromCharCode(event.keyCode));}
els[i].onpaste = function(){return !clipboardData.getData('text').match(/\D/);}
els[i].ondragenter = function(){return false;}
els[i].style.imeMode= "disabled";
break;
case "float-":
case "num-":
els[i].onkeypress = function(){return /[\-\.]|\d/.test(String.fromCharCode(event.keyCode));}
els[i].onpaste = function(){return !clipboardData.getData('text').match(/\D/);}
els[i].ondragenter = function(){return false;}
els[i].style.imeMode= "disabled";
break;
case "ascii":
els[i].style.imeMode= "disabled";
break;
case "ip4":
els[i].onkeypress = function(){return /[\.]|\d/.test(String.fromCharCode(event.keyCode));}
els[i].onpaste = function(){return !clipboardData.getData('text').match(/\D/);}
els[i].ondragenter = function(){return false;}
els[i].style.imeMode= "disabled";
els[i].maxLength = 15;
break;
case "color":
els[i].onkeypress = function(){return
/[a-fA-Z]|\d/.test(String.fromCharCode(event.keyCode))||(this.value.indexOf('#')<0?String.fromCharCode(event.keyCode)=="#":false);}
els[i].onpaste = function(){return !clipboardData.getData('text').match(/\D/);}
els[i].ondragenter = function(){return false;}
els[i].maxLength = 7;
els[i].style.imeMode= "disabled";
break;
case "date":
els[i].onkeypress = function(){return /[\/\-\.]|\d/.test(String.fromCharCode(event.keyCode));}
els[i].onpaste = function(){return !clipboardData.getData('text').match(/\D/);}
els[i].ondragenter = function(){return false;}
els[i].style.imeMode= "disabled";
break;
}
}
}
}
}
}
//初始化
var g_check = new CLASS_CHECK();
g_check.keyCheck();
面对权限,我们要解决几个的问题。
第一个就是:我们的软件里面有哪些功能? —— 给用户自己维护角色作准备
比如添加新闻、添加产品、客户信息维护、合同管理等等,当然还可以细分一下,
客户信息维护又可以分为:客户基本信息、客户的联系人、客户报价、客户的合同等。
我的习惯是建立一个表,叫做功能结点表。
这个表可以生成左面的功能树,也可以记录项目里面一共有哪些功能。
这里的一个功能指的是两个页面,一个是列表页面,一个是表单页面。
列表页面包括查询、导出数据等功能,表单页面又可以再往下继续划分,就是可以在包含子功能结点。
这样一个项目里的功能就全部记录到了一个表里面。
功能结点表的主要字段
FunctionID
ParentID
Title
URL
...
其它字段略。
第二个问题:哪些人可以访问到那些功能结点?
这个问题呢就要引入“角色”或者“用户组”的概念了。
我们建立一个角色表来记录一个角色拥有的功能结点。
我们可以写一个程序,让客户自己来维护,也就是说用户可以自己添加、修改“角色”。
然后把人员和角色关联起来就可以了。
角色表的主要字段
RoleID
TItle
角色拥有的功能节点表得主要字段
Role_FunctionID
RoleID
FunctionID
第三个问题:详细权限的划分
一个页面可能会有很多的功能,比如可以查看、查询、添加数据、修改数据、删除数据(又可以分为逻辑删除和物理删除)、导出数据等。
而一个人(或者说是角色)来到这个页面后(获得了访问权限),不一定会拥有上面的全部的权限。
这样就需要再详细区分一下。
这样的话我们在加一个字段就可以了,通过这个字段来判断登录人有哪些具体的权限。
角色拥有的功能节点表得主要字段
Role_FunctionID
RoleID
FunctionID
详细权限
第四个问题:资源的访问权限
这个我还没有想好怎么解决。
就是说同一个页面,业务员只能看到自己的客户信息、并且可以维护,
然后业务一部门的经理只能看到业务一部门的业务员的客户信息,不能看到其他业务部的客户信息
再然后是业务部的总经理,他可以看到所有业务员的客户信息。
最后就是,不知道大家有没有遇到过“内勤”这个职位,就是说业务员只管联系客户,不用自己录入客户信息,
而往电脑里录入信息的工作就全交给内勤了。
再往下说就更烦了,一个业务部可能只有一个内勤,也可能有多个内勤,一个内勤会对应多个业务员。
好了先不说了,好像有点跑题了,这个就当作是特利吧。
前三个问题都不需要引入部门的概念,但是第四个问题就不得不考虑部门了。
以上是我的思路,不知道能不能把权限的问题,从粗粒度上说清楚。
有不对的请指出,大家一起研究。
写完了就要提交了,想向上传一个图片,IE居然死掉了,白写了,还得重来一遍。郁闷。
上篇:
通用权限的思路。只是一个简单的思路。
上一篇住要是想说一下大体的思路,就是一个主要的框架,我觉得在做一件事情之前,都需要有一个初略的设计,就是中提的想法,抓住问题的关键点。所以对于权限问题,我觉得需要抓住下面的这四个问题;
1、我们的软件里面有哪些功能?
2、哪些人可以
访问到哪些功能结点?
3、访问到了页面后可以做哪些事情?(查询、添加、修改、删除、导出、打印等)
(原来的说法:详细权限的划分)
4、在同一个页面里哪些人可以看到那些信息
(原来的说法:资源的访问权限)
这是我的个人见解,是通过几个项目总结出来的,如果不全面、或者不正确的话,欢迎大家及时指正,共同努力、共同提高!
我设计了下面的这几个表,来解决这些问题。这里我只想表示表之间的关联,至于字段我只是写了几个主要的,字段的设计嘛,大家肯定各有各的方式,我想我写出来主要的就可以了。
我的英文比较差,还是直接用中文吧,这样更直接一些。
如果看图不太清楚的话,可以到这里下载 visio 格式的文件。
http://www.cnblogs.com/jyk/archive/2008/04/25/1170979.html
先来看
第一个问题,[项目—功能结点] 和
[项目—节点拥有的详细权限] 这两个表记录了项目里面有哪些功能结点和详细的功能,
[项目—功能结点] 功能节点,可以通过这个表来呈现左面的功能树。记录打开的页面和相关的信息。
[项目—节点拥有的详细权限] 按钮组,一个功能节点(主要是列表页面)有哪些按钮,比如“添加”按钮,“修改”按钮等。记录按钮的名称、打开的页面和相关的信息。
这两个表是在设计阶段完成的,程序员可以根据这个来实现功能。
解决了第一个问题后,
第二个问题就好办了,
[项目—角色] 和
[角色拥有的功能结点] 来 实现。
[项目—角色] 记录项目里面有哪些角色。
[角色拥有的功能结点] 记录一个角色拥有哪些功能结点,功能结点里面有哪些具体的操作(添加、修改等)
不知道大家的项目的角色是在什么时候诞生的,是在设计的时候吗?还是程序做好之后由用户自己设计?我的做法是后者。因为客户比我们更清楚需要多少角色,需要什么样的角色,一个角色里有哪些功能更合适。
我们可以做一个维护程序,让客户自行添加。先在角色表里添加一个角色,然后选择角色可以看到的功能页面,最后选择在这个功能结点里可以做的操作(添加、修改等)。这样一个角色就诞生了。
角色有了之后,就可以给人员分配角色,或者是往角色里添加人员。这样就解决了哪些人可以访问哪些功能节点的问题。
其实在设计角色的时候就把
第三个问题也搞定了。
客户添加角色的截图
操作也是比较方便的,当点击“计划和日志”前面的方框(打对号)的时候会自动展开下面的子结点和子子节点,然后这些节点都会被选中,后面的按钮也会被选中。
当选中“工作日志”的时候,上面的节点和后面的按钮也会被选中。
上面的信息全部来自数据库,也就是第一个图里的哪些表。
第四个问题还没有更好的解决方法,目前只能在程序里面硬编码。
Posted on 2008-05-02 19:19
jeff377 阅读(1192)
评论(13) 编辑 收藏 所属分类:
ASP.NET
2008年4月29日
很多朋友都希望这份pdm文件可以放到网上,目前这份文件我已经拿到,供大家学习之用,希望大家共同进步。
pdm下载:/Files/wxj1020/Sample.rar
上周参加了高级数据库的培训,觉得主讲人水平很高,我受益颇多。数据库设计的工具采用PowerDesigner,这个工具以前没有怎么用过,不是很熟悉,这次结合实例来学习,对我帮助很大。现在我把这部分资料整理到网上,以便像我一样的初学者学习。
售票系统的需求分析如下:
简介
售票系统主要用于车站日常的票务处理。售票系统是车站信息管理系统数据的主要来源,它的业务直接影响财务、检票、调度系统。售票系统主要完成售票、远程售票、废票、取消废票功能、改签票、退票、取消退票功能、补票、售票查询和票款结算单填写等功能。
说明
售票
正常出售本站所有车次车票,并按要求打印出车票。
远程售票
正常出售远程车站所有车次车票,并按远程车站的要求打印出车票。
废票
在正常售票过程中, 可能因操作失误或打印机械故障等原因造成错票而作废车票。
取消废票
当废票时,发现废的票是不应该废的,即为正常票,需要通过取消废票来实现。
远程废票
对所售远程车票作废票处理。
退票
在正常退票时间内办理退票,并根据退票规定收取一定的手续费。
远程退票
对所售远程车票作退票处理。
取消退票
当退票时,发现退的票是不应该退的,即为正常的票,需要通过取消退票来实现。
改签票
由于乘客或车站某种原因需对已出售的车票作更换车次的车票。改签仅允许改签一次。
远程改签票
对所售远程车票作改签处理。
注销票
删除某些由于调试程序所用的票记录及其影响。
补票
只能在超过售票时限后进行,且在未发车之前,在统计时注意对非微机售票的票的处理(例如:剪刀票)。
售票查询
通过一定的授权和设定售票员可以查询在设定时间范围内或多少张票以前的出售、作废、改签和退票的情况,并对有权限的人员可有累计票款服务。
票款结算单
售票员每天售完车票后填写每日结算单,以便财务人员查询。
重打车票
在正常售票过程中, 可能因打印机械故障等原因造成错票而重新打印车票。
售票类型
(1)指印刷票面格式和打印内容
所有车站均使用一种票面格式。车票采用主、副联格式,使用条码自动检票的地方可以不使用副联,人工检票的车站必须使用幅联,互联售票时车票式样以车次所在站的设定为准。
车票式样
车票有一正联(旅客用作乘车凭证),一至二副联(在没有自动检票的车站用作生成结算小路单的凭证,还用于和售票员的对帐)。车票中所有联都
应有的信息为车次、起止站点、票价、发车日期时间,而正联所特有的信息为售票员工号、检票口、是否改签票、车票上应有的信息:车票固有票号、计算机打印的
票号、票号条形码(仅用于自动检票的车站)。
副联可按系统设置打印或不打印,条形码也可按系统设置打印或不打印(或直接印刷)。
(2)车票类型
1)正常班次车票:发售的当天或预售正常轮班车次的车票。
2)远程售票:发售远程车站车次的车票,其打印格式同远程车站。
3)流水班次车票:发售的流水班次车票。
4)退票和改签票:
退票分为
A、正常退票。办理标准规定退票时间内的退票。
B、强行退票。在超过时限后退票,即强行退票。
补票:补票只能在超过售票时限后进行,且必须在未发车之前。
订票:设定定票有效期限,在有有效时限保留座位,有效时外释放座位等。
票种的定义
①全价票
②半价票 允许设定可用标志,自由定义名称。
③免票 只允许设定可用标志。
④优惠票1-3 允许设定可用标志,自由定义名称。
⑤折扣票 对原票价的基础上进行一定的打折,有严格的权限控制。
车票属性
是否流水车票的所属车站票种是否改签票状态是否预留预定非流水票本地票全票非改签票已售出非预留,预定票流水票远程票半票改签票已退预留票免票已废预定票优惠票1-3已被改签折扣票售票窗口设置
通过设置权限和模式分开和组合将售票窗口、退票窗口、补票窗口等分开。
总体功能结构图
根据以上需求分析设计的实体如下(如果想自己用一下PowerDersigner设计数据库的话,请暂且不要看下面的内容):
设计的部分表如下:
根据理物模型建库:
希望这部分资料能对某些初学者有一定帮助。
2008年4月25日
js:########################hidden "tab_view"表格
function display(obj)
{
var regx=/open.gif/;
var str=obj.src;
if(regx.test(str))
{
obj.src="image/close.gif";
obj.parentNode.parentNode.parentNode.parentNode.rows(1).style.display="none";
}
else
{
obj.src="image/open.gif";
obj.parentNode.parentNode.parentNode.parentNode.rows(1).style.display="block";
}
}
///
html######################################
<table class="dragTable" id="tab1" style="BORDER-RIGHT: red 0px dashed; BORDER-TOP: #3366cc 1px solid; DISPLAY:block; Z-INDEX: 1; BORDER-LEFT: red 0px dashed; BORDER-BOTTOM: red 0px dashed"cellSpacing="0" border="0">
<tr class="dragTR">
<td><img id="img1" onclick="display(this)" src="image/open.gif" align="left">
<asp:label id="class_name" Runat="server">自定义网址收藏夹</asp:label>
<img onclick="dele_class(this)" src="image/dele.gif" align="right">
<IMG style="WIDTH: 16px; HEIGHT: 8px" onclick="return dropdownmenu(this, event, menu2, '200px')"
onmouseout="delayhidemenu()" height="8" src="image/edit.gif" width="16" align="right">
</td>
</tr>
<tr>
<td>
<table id="tab_view" borderColor="gainsboro" cellSpacing="0" cellPadding="0" align="center"
bgColor="white" border="0">
<tr>
<td width="200px" height="15px">
</td>
</tr>
</table>
</td>
</tr>
</table>
2008年4月24日
代码注入是最常见的一种安全漏洞,也是最容易受到攻击的,因为攻击者不需要掌握太多的专业知识就可以对这些漏洞进行攻击。对系统的威胁也非常大。代码注入攻击主要包括SQL 注入、跨站点脚本和缓冲区溢出三种:
SQL注入(SQL injection):根据用户的输入值来动态构造SQL语句,该构造语句很可能就是攻击性的有害SQL语句。即攻击者可发送SQL输入来更改数据库中的预期查询或执行全新的查询。表单身份验证登录页是常见的攻击对象,因为查询用户存储所使用的是用户名和密码。
跨站点脚本(Cross-site Scripting):将恶意脚本作为输入项发送到 Web 应用程序。一旦执行,结果将回应至用户浏览器。
缓冲区溢出(Buffer Overflow):虽然托管代码的类型安全验证可大大降低风险,但应用程序依然存在安全漏洞,特别是调用非托管代码时。缓冲区溢出使攻击者可利用 Web 应用程序的安全上下文在 Web 应用程序进程中执行任意代码。
这里我们要特别搞清楚一个概论,传输加密技术不能用来防御注入攻击。注入攻击可通过使用HTTP或HTTPS Secure Socket Layer(SSL) 连接发送请求。收到信息的服务器会认为该信息是合法的输入。
2.2信息泄漏
信息泄漏主要涉及把一些服务器出错信息完全透露给请求者和隐藏域信息被攻击者使用等。攻击者往往通过探测 Web页来找寻引起异常的各种情况,异常细节信息常以 HTML 的形式返回并显示在浏览器中。这可能会泄漏很有用的信息,如堆栈跟踪。堆栈跟踪包含数据库连接字符串、数据库名、数据库方案信息、SQL 语句以及操作系统和平台版本。攻击者利用这些信息很容易找到攻击的方法。
2.3身份盗用
恶意用户盗用合法用户的身份访问网站系统,主要包括身份验证Cookie未加密的保存在客户端,系统中用户使用了弱密码等。攻击者采用这些信息以合法的用户身份登录系统。
3 预防措施
知道了攻击者怎么攻击Asp.Net网站系统,系统在编写第一句代码的时候就需要考虑安全性问题,防止系统漏洞的出现。不给攻击者攻击的机会。
3.1防Sql注入
先来看一下以下语句:
SqlDataAdapter UserQuery = new SqlDataAdapter( "Select * From Users Where UserName='" + username + "' And Password='" + password, conn);
验证登录用户名和密码,正常输入没有什么问题。但如果username输入“Or 1=1”,密码无论输入什么都可以进入系统。更为可怕的是如果连接数据库的用户权限足够大,还可以通过“;exec master..xp_cmdshell 命令 –-” 执行任意Shell命令。
对于SQL注入的预防,可以通过很多种技术来解决。
第一:对于一定需要动态构造SQL查询的场合,可以通过格式化用户输入的信息来避免SQL注入,即可以替换输入信息中的单引号,即把所有单独出现的单引号改成两个单引号,防止攻击者修改SQL命令的含义;可以删除用户输入内容中的所有连字符,防止攻击者构造出类如“Select * From Users Where UserName = 'Dean' -- And Password =''”之类的查询,因为这类查询的后半部分已经被注释掉,不再有效,攻击者只要知道一个合法的用户登录名称,根本不需要知道用户的密码就可以顺利获得访问权限。
第二:用存储过程来执行所有的查询。SQL参数的传递方式将防止攻击者利用单引号和连字符实施攻击。此外,它还使得数据库权限可以限制到只允许特定的存储过程执行,所有的用户输入必须遵从被调用的存储过程的安全上下文,这样就很难再发生注入式攻击了。
第三:检查用户输入的合法性,确信输入的内容只包含合法的数据,并限制表单或查询字符串输入的长度。数据检查应当在客户端和服务器端都执行——之所以要执行服务器端验证,是为了弥补客户端验证机制脆弱的安全性。
第四:限制用来执行查询的数据库帐户权限,不让其执行一些特殊的存储过程和访问不该访问的资源。对查询所返回的记录数量也作一些判断,如程序只要求返回一个记录,但实际返回的记录却超过一行,那就当作出错处理。
3.2不要相信用户输入
在Web应用开发中,开发者最大的失误往往是无条件地信任用户输入,假定用户(即使是恶意用户)按正常情况进行操作并总是受到浏览器的限制,总是通过浏览器和服务器交互,给攻击者打开了方便之门。
<%@ Page Language="C#" ValidateRequest="false" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = TextBox1.Text;
}
</script>
<form runat="server">
<asp:Label ID="Label1" runat="server"/>
<asp:TextBox ID="TextBox1" runat="server"/>
<asp:Button ID="Button1" OnClick="Button1_Click" runat="server" Text="提交"/>
</form>
上
面的代码简单,但却有一个非常严重的安全漏洞。比如用户输入
“<script>alert(document.cookie)</script>”。堵住该漏洞需要使用ASP.NET请求
验证,在页面的@Pages元素里面设置ValidateRequest = "True"来打开请求验证,默认情况下Asp.Net
1.1和2.0该项设置是打开的。ASP.NET请求验证会对送至服务器的数据检测是否含有HTML标记元素和保留字符。这可以防止用户向程序中输入脚
本。请求验证会对照一个有潜在威胁的字符串列表进行匹配,如果发现异常它会抛出一个HttpRequestValidationException类型的
异常。
有些情况,需要准许用户输入特定HTML代码。这就需要过滤输入数据使它们在输出的时候没有危险,使用HttpUtility.HtmlEncode方法先对输入值进行编码,再替换特定的HTML标记。例如前面的例子,如果准许用户输入<b>标记:
StringBuilder sb = new StringBuilder(HttpUtility.HtmlEncode(TextBox1.Text));
sb.Replace("<b>", "<b>");
sb.Replace("</b>", "</b>");
对不安全的URL地址也要使用HttpUtility.UrlEncode进行编码。因此,只有严密地验证用户输入的合法性,才能有效地抵抗黑客的攻击。
3.3让隐藏域更安全
在ASP.NET应用中,几乎所有HTML页面都有__VIEWSTATE隐藏域,它储存着来自页面控件的动态数据、开发者在ViewState中显式保存的数据以及它们数据的密码签字。如图1所示:

图1:页面__VIEWSTATE值
由
于__VIEWSTATE是BASE 64编码的,所以往往被忽略,但黑客可以方便地解码BASE
64数据,轻松的得到__VIEWSTATE提供的详细资料。通过设置EnableViewStatMAC="True"来加密ViewState数据。
启用__VIEWSTATE数据加密功能,可以在页面级别上设置 EnableViewStateMAC,也可以在应用程序级别上设置。然后还需将machineKey验证类型设置成3DES,要求ASP.NET用3DES对称加密算法加密ViewState数据。
3.4加密身份验证
ASP.NET身份验证方式有"Windows"、 "Forms"和"Passport" 。"Forms"可以为用户提供一个输入凭据的登录窗体,即平常见到的输入用户名和密码窗体。出于身份验证的灵活性和系统独立性,多数Web应用程序都采用Form验证方式。
攻击者可以利用 XSS 窃取身份验证的Cookie进行查看或修改。为了增强Cookie 的隐私性和完整性,可以在 <forms> 元素中设置 protection 属性,如下:
<forms protection="All"
指明应用程序同时使用数据验证和加密方法来保护Cookie。第二限制身份验证Cookie 的生命周期,可以减少攻击者使用盗用的 cookie 骗取应用程序访问权限的时间窗口。如下:
<forms timeout="10"
最
后在 <forms> 元素中使用唯一的 name 和 path
属性值。如果名称唯一,可在同一服务器驻留多个应用程序时防止出现潜在的问题。例如,如果不使用独特的名称,通过某一应用程序身份验证的用户无需重定向至
另一应用程序的登录页即可请求访问该应用程序。
另外在创建身份验证票据时也应该加密票据,有效防止身份被窃取。如下:
System.Web.Security.FormsAuthenticationTicket
tk=new
System.Web.Security.FormsAuthenticationTicket(1,"Admin",System.DateTime.Now,
System.DateTime.Now.AddYears(1),false,"测试用户数据");
String str=System.Web.Security.FormsAuthentication.Encrypt(tk);//加密身份验证票据
3.5防止信息泄漏
攻击者在攻击网站应用系统前,首先需要获取网站应用系统的信息。他们通常向Web应用服务器抛出很多请求,引发Web错误抛出异常,从而获得很多对攻击者有用的信息。
在
部署网站的时候,不要把错误信息返回给客户端,可以在Web.config使用<customErrors>元素来配置,一般的错误信息应该
被程序错误检测机制返回到客户端。确认已经更改customErrors中的mode属性为"RemoteOnly",指明IIS只对不在本地 Web
服务器上运行的用户显示自定义(友好的)信息。代码示例为:
<customErrors mode = "RemoteOnly">
也可以通过设置defaultRedirect指定自定义错误页,如下:
<customErrors mode = "on" defaultRedirect = "Error.htm">
另
外一方面,网站的数据库连接字符串经常写在Web.config配置文件里面。但是很多网站往往是直接把连接字符串写入,用户名和密码以明文的方式保存。
也就是拿到配置文件就相当于完全掌握了数据库。可以通过System.Security.Cryptography在加密存储。需要用得时候在通过它解
密。
4 结束语
知道系统的安全漏洞在什么地方,在开发系统时编写第一句代码就考虑安全性问题。采用Asp.Net的先进技术,防止漏洞的出现。随着Asp.Net技术的广泛应用,网站的逐步普及。如何采用Asp.Net构建安全性网站必将受到人们的高度重视。
参考文献:
[1]http://www.microsoft.com/china/msdn.
[2]http://www.cnblogs.com.
2008年4月22日
2007/12/19 03:13
1: 过滤首尾空格trim、2:过滤左边空格ltrim 3:过滤右边空格
一:用正则方法写成三个函数.
<script type="text/javascript">
function trim(str){ //删除左右两端的空格
return str.replace(/(^\s*)|(\s*$)/g, "");
}
function ltrim(str){ //删除左边的空格
return str.replace(/(^\s*)/g,"");
}
function rtrim(str){ //删除右边的空格
return str.replace(/(\s*$)/g,"");
}
</script>
2008-04-07 15:18
元字符
现在你已经知道几个很有用的元字符了,如\b,.,*,还有\d.当然还有更多的元字符可用,比如\s匹配任意的空白符,包括空格,制表符(Tab),换行符,中文全角空格等。\w匹配字母或数字或下划线或汉字等。
下面来试试更多的例子:
\ba\w*\b匹配以字母a开头的单词——先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)(好吧,现在我们说说正则表达式里的单词是什么意思吧:就是几个连续的\w。不错,这与学习英文时要背的成千上万个同名的东西的确关系不大)。
\d+匹配1个或更多连续的数字。这里的+是和*类似的元字符,不同的是*匹配重复任意次(可能是0次),而+则匹配重复1次或更多次。
\b\w{6}\b 匹配刚好6个字母/数字的单词。
表1.常用的元字符
| 代码 |
说明 |
| . |
匹配除换行符以外的任意字符 |
| \w |
匹配字母或数字或下划线或汉字 |
| \s |
匹配任意的空白符 |
| \d |
匹配数字 |
| \b |
匹配单词的开始或结束 |
| ^ |
匹配字符串的开始 |
| $ |
匹配字符串的结束 |
元字符^(和数字6在同一个键位上的符号)以及$和\b有点类似,都匹配一个位置。^匹配你要用来查找的字符串的开头,$匹配结尾。这两个代码在验证输入的内容时非常有用,比如一个网站如果要求你填写的QQ号必须为5位到12位数字时,可以使用:^\d{5,12}$。
这里的{5,12}和前面介绍过的{2}是类似的,只不过{2}匹配只能不多不少重复2次,{5,12}则是重复的次数不能少于5次,不能多于12次,否则都不匹配。
因为使用了^和$,所以输入的整个字符串都要用来和\d{5,12}来匹配,也就是说整个输入必须是5到12个数字,因此如果输入的QQ号能匹配这个正则表达式的话,那就符合要求了。
和忽略大小写的选项类似,有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项,^和$的意义就变成了匹配行的开始处和结束处。
字符转义
如果你想查找元字符本身的话,比如你查找.,或者*,就出现了问题:你没法指定它们,因为它们会被解释成其它的意思。这时你就必须使用\来取消这些字符的特殊意义。因此,你应该使用\.和\*。当然,要查找\本身,你也得用\\.
例如:www\.unibetter\.com匹配www.unibetter.com,c:\\Windows匹配c:\Windows。
重复
你已经看过了前面的*,+,{2},{5,12}这几个匹配重复的方式了。下面是正则表达式中所有的限定符(指定数量的代码,例如*,{5,12}等):
表2.常用的限定符
| 代码/语法 |
说明 |
| * |
重复零次或更多次 |
| + |
重复一次或更多次 |
| ? |
重复零次或一次 |
| {n} |
重复n次 |
| {n,} |
重复n次或更多次 |
| {n,m} |
重复n到m次 |
下面是一些使用重复的例子:
Windows\d+匹配Windows后面跟1个或更多数字
13\d{9}匹配13后面跟9个数字(中国的手机号)
^\w+匹配一行的第一个单词(或整个字符串的第一个单词,具体匹配哪个意思得看选项设置)
字符类
要想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?
很简单,你只需要在中括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母,[.?!]匹配标点符号(.或?或!)(英文语句通常只以这三个标点结束)。
我们也可以轻松地指定一个字符范围,像[0-9]代表的含意与\d就是完全一致的:一位数字,同理[a-z0-9A-Z_]也完全等同于\w(如果只考虑英文的话)。
下面是一个更复杂的表达式:\(?0\d{2}[) -]?\d{8}。
这个表达式可以匹配几种格式的电话号码,像(010)88886666,或022-22334455,或02912345678等。我们对它进行一些分析吧:首先是一个转义字符\(,它能出现0次或1次(?),然后是一个0,后面跟着2个数字(\d{2}),然后是)或-或空格中的一个,它出现1次或不出现(?),最后是8个数字(\d{8})。不幸的是,它也能匹配010)12345678或(022-87654321这样的“不正确”的格式。要解决这个问题,请在本教程的下面查找答案。
反义
有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义:
表3.常用的反义代码
| 代码/语法 |
说明 |
| \W |
匹配任意不是字母,数字,下划线,汉字的字符 |
| \S |
匹配任意不是空白符的字符 |
| \D |
匹配任意非数字的字符 |
| \B |
匹配不是单词开头或结束的位置 |
| [^x] |
匹配除了x以外的任意字符 |
| [^aeiou] |
匹配除了aeiou这几个字母以外的任意字符 |
例子:\S+匹配不包含空白符的字符串。
<a[^>]+>匹配用尖括号括起来的以a开头的字符串。
替换
好了,现在终于到了解决3位或4位区号问题的时间了。正则表达式里的替换指的是有几种规则,如果满足其中任意一种规则