一种安全、高效、简单的登陆算法:
现在微软的vs2005中自带的membership已经能够很好的满足大多数场景下的用户管理的需求。但是某些特殊情况下我们还须要订制的用户登录系统,比方说为了效率、安全等。
另外即使只是做做试验也是不错的尝试。
本文并没有对各种登陆算法进行对比探讨。而是仅仅给出了一个比较安全、高效也经历了实际的考验的算法供大家参考。
本文中给出一个实例的节选,希望能给各位启示。
该算法的特点为:
1、安全、仅仅使用用户名(考生号)来访问数据库,这样需要控制的项就比较少了.即使进行安全检查系统压力也小。
2、高效、一般情况下用户操作仅需一次数据访问,所需的数据就全部返回了。而且单键访问数据执行效率也高。
3、比较完整的简单的分层设计,可以供入门程序员参考(作为教学人员,我的注释写的很详细,具有一定的可读性)

一、建立数据模型:数据集如下图:存放在app_code目录中,命名为kaoshengs.xsd
1、考生表的结构。依次为考生号、考生姓名、录取结果、密码(口令,默认是空值。)、查分密码(该系统中每个考生有自己的随机查分密码覆膜印在准考证上,第一次需要使用该密码来登录系统并修改密码)、报名点代码、县区代码、地市代码(后边3个代码是为了管理使用)。
2、适配器。增加了几个访问数据的方法,有名称和参数可以很容易的看出,不再解释了。

二、对该考生对象的操作的类库。存放在app_code目录中,命名为kaosheng.cs
需要注意的是,该系统在登陆时需要的操作比较多
1、有用户名和密码能够登录
2、有录取结果,显示录取结果,并不再允许继续操作
3、密码为空,则为第一次登录允许使用查分密码来修改口令
4、修改已有的口令
  1using System;
  2using System.Data;
  3using System.Configuration;
  4using System.Web;
  5using System.Web.Security;
  6using System.Web.UI;
  7using System.Web.UI.WebControls;
  8using System.Web.UI.WebControls.WebParts;
  9using System.Web.UI.HtmlControls;
 10using System.Collections;
 11
 12/// <summary>
 13/// kaosheng 的摘要说明
 14/// edit by lww @2007-04-21
 15/// 考生对象的相关操作
 16/// </summary>

 17public class kaosheng
 18{
 19    public kaosheng()
 20    {
 21        //
 22        // TODO: 在此处添加构造函数逻辑
 23        //
 24    }

 25
 26    /// <summary>
 27    /// 验证用户
 28    /// </summary>
 29    /// <param name="ksh"></param>
 30    /// <param name="code"></param>
 31    /// <returns>
 32    /// 根据返回进行跳转
 33    /// 返回值为arrylist 其中
 34    /// retal[0]存放int形式的返回值 
 35    /// 其中 
 36    /// 0:系统出错、数据库连接?
 37    /// 1:已被录取
 38    /// 2:登录正确
 39    /// 3:首次登录需导向修改页面
 40    /// 4:登录密码不对(已有但是不正确)
 41    /// 5:未更改过密码,并且查分码不对
 42    /// 6:错误的考生号
 43    /// retal[1]之后存放string附加值
 44    /// 标号     0              1       2       3       4          5           6
 45    /// 包括:   返回类型   考生号、考生姓名、县区、 地市
 46     
 47    /// </returns>    

 48    public static ArrayList yanzheng(string ksh,string code)
 49    {
 50        ///根据输入的考生号和密码进行判断
 51        ///由于这里需要判断的项比较多,所以采用将考生相关信息返回的方式
 52        ///根据输入的考生号,得到返回值。包括:考生姓名、密码、查分密码、录取信息

 53        ArrayList ksinfo=  getks(ksh);
 54        ArrayList retal = new ArrayList();
 55        if (ksinfo[0].ToString().Trim() == "0")
 56        {//错误考生号
 57            retal.Add("6");
 58            return retal;
 59        }

 60        ///1、首先判断是否被录取,如果被录取则提示,不再继续进行下面的操作
 61        if ((ksinfo[4].ToString().Trim() != string.Empty) && (ksinfo[4].ToString().Trim() != null))
 62        {//已经被录取
 63            retal.Add("1");
 64            retal.Add(ksinfo[4].ToString());
 65            return retal;
 66        }

 67        else
 68        {//本来下边的处理其他情况的代码包含在else中,但是为了程序的可读性,我们在前边增加了 return retal;跳出,
 69         //这样后边就可以从if else中剥离出来了.这里为了方便叙述保留了else段
 70        }

 71        ///ksinfo格式
 72        /// 根据考生号取得考生信息
 73        /// 标号     0       1       2       3       4          5           6           7
 74        /// 包括:考生号、考生姓名、密码、查分密码、录取信息    县区        地市    报名点
 75
 76        ///3、如果返回值密码项不为空,判断是否和新输入的密码的md5一致,一致则允许登录,不一致,则导向提示页面,提示考生联系管理员

 77        if ((ksinfo[2].ToString().Trim() != string.Empty) && (ksinfo[2].ToString().Trim() != null))
 78        {
 79            if (ksinfo[2].ToString().Trim() == MD5s.genmd5(code))
 80            {//正确登录
 81                retal.Add("2");                   
 82            }

 83            else
 84            {
 85                retal.Add("4");
 86            }

 87        }

 88        else
 89        {  
 90            ///4、如果为空,判断查分密码是否正确,正确则导向修改密码页面,
 91            if (ksinfo[3].ToString().Trim() == code)
 92            {
 93                retal.Add("3");                    
 94            }

 95            else 
 96            {
 97                retal.Add( "5");
 98            }
                
 99        }

100        //返回结果:附加
101        retal.Add(ksinfo[0].ToString());//考生号
102        retal.Add(ksinfo[1].ToString());//考生姓名
103        retal.Add(ksinfo[3].ToString());//考生登录号
104        retal.Add(ksinfo[5].ToString());//考生县区名称
105        retal.Add(ksinfo[6].ToString());//考生地市名称
106        retal.Add(ksinfo[7].ToString());//报名点名称
107        retal.Add(ksinfo[8].ToString());//报名点代码
108        /// 返回结果 
109          /// 标号     0       1       2       3                4          5           6           
110        /// 包括:    代码   考生号、考生姓名、查分密码、      县区        地市    报名点

111        return retal;
112    }

113    /// <summary>
114    /// 修改密码:重载1、考生使用
115    /// </summary>
116    /// <param name="ksh"></param>
117    /// <param name="code"></param>
118    /// <returns></returns>

119    public static bool passchange(string ksh, string code,string newcode)
120    {
121        ///根据考生号进行密码重置,
122        ///首先判断查分密码是否正确,然后判断更改密码
123        ///如果密码为空,根据查分密码判断

124        ArrayList ksinfo = getks(ksh);
125        if (ksinfo[2].ToString().Trim() == string.Empty)
126        {
127            if ((ksinfo[0].ToString() == ksh) && (ksinfo[3].ToString() == code))
128            {
129               return passchange(ksh, newcode);
130            }

131        }

132        else
133        {
134            if ((ksinfo[0].ToString() == ksh) && (ksinfo[2].ToString().Trim() == MD5s.genmd5(code)))
135            {
136                return passchange(ksh, newcode);
137            }
 
138        }

139        return false;
140    }

141    /// <summary>
142    /// 重置密码:重载2、管理员使用,将考生密码初始化为指定值
143    /// </summary>
144    /// <param name="ksh"></param>
145    /// <returns></returns>

146    public static bool passchange(string ksh, string newcode)
147    {
148        ///根据考生号进行密码重置
149        kaoshengsTableAdapters.tksTableAdapter ksapt = new kaoshengsTableAdapters.tksTableAdapter();
150        string newpass = "";
151        //逻辑,置空密码传入的时候需要处理,不需要md5
152        if(newcode.Trim()==string.Empty)
153        {
154            newpass = "";
155        }

156        else
157        {
158            newpass = MD5s.genmd5(newcode);
159        }

160        try
161        
162            ksapt.passchange(newpass, ksh);
163            return true;
164        }

165        catch return false; }        
166    }

167
168    /// <summary>
169    /// 登录日志
170    /// </summary>
171    /// <param name="ksh"></param>
172    /// <param name="code"></param>
173    /// <returns></returns>

174    public static string loginlog(string ksh, string ip,string code)
175    {
176        ///写日志
177            rizhiTableAdapters.rzTableAdapter rz = new rizhiTableAdapters.rzTableAdapter();
178            rz.rizhi(ksh, ip, code);
179          return "ok";
180    }

181
182    /// <summary>
183    /// 根据考生号取得考生信息
184    /// 请注意,该命令仅仅使用ksh作为参数而没有使用用户名,
185    /// 原因为
186    /// 1、安全:ksh已经在网页中限制为仅能为数字
187    /// 2、高效:数字的主键访问速度快
188    /// 返回信息为:    /// 
189    /// 标号     0       1       2       3       4          5           6           7       8
190    /// 包括:考生号、考生姓名、密码、查分密码、录取信息    县区        地市    报名点      报名点代码
191    /// </summary>
192    /// <param name="ksh"></param>

193    public static ArrayList getks(string ksh)
194    {
195        ///从数据库中根据考生号填充数据然后将arraylist填充        
196        kaoshengsTableAdapters.tksTableAdapter ksapt = new kaoshengsTableAdapters.tksTableAdapter();
197        kaoshengs.tksDataTable tks=new kaoshengs.tksDataTable();
198        ksapt.getksinfo(tks, ksh);
199        kaoshengs.tksRow r = tks.FindByksh(ksh);
200        ArrayList al = new ArrayList();
201        if (r==null)
202        {//未发现记录,返回第一个为"0"标记为错误
203            al.Add("0");
204            return al;
205        }

206        else
207        {            
208            al.Add(r.ksh.ToString().Trim());//考生考生号        
209            al.Add(r.ksxm.ToString().Trim());//考生姓名 
210            al.Add(r.mima.ToString().Trim());//考生当前密码
211            al.Add(r.kscf.ToString().Trim());//考生查分密码
212            al.Add(r.kslq.ToString().Trim());//考生是否被录取  
213            ///在这里返回的区域代码需要翻译为含义
214            string xqdm = r.bmddm.Substring(04).Trim();
215            string dsdm = r.bmddm.Substring(02).Trim();
216            string bmddm = r.bmddm.Substring(0,6).Trim();
217            string xqmc = cache.transxqdm(xqdm);
218            string dsmc = cache.transdsdm(dsdm);
219            string bmdmc = cache.transbmddm(bmddm);
220            al.Add(xqmc);//县区名称
221            al.Add(dsmc);//地市名称
222            al.Add(bmdmc);//报名点名称
223            al.Add(r.bmddm.Substring(06).Trim());//报名点代码
224            return al;
225        }

226    }

227
228    //下边的代码为管理员使用的部分 
229  
230    /// <summary>
231    /// 通过考生号加载数据
232    /// </summary>
233    /// <param name="ksh"></param>
234    /// <returns></returns>

235    public static kaoshengs.tksDataTable getbyksh(string ksh)
236    {
237        //首先取出当前的权限
238        ArrayList alqx = quanxian.yanzheng();
239        kaoshengs.tksDataTable tb = new kaoshengs.tksDataTable(); 
240        kaoshengsTableAdapters.tksTableAdapter ksapt = new kaoshengsTableAdapters.tksTableAdapter();
241        tb = ksapt.GetDataByksh(ksh, alqx[3].ToString(), alqx[4].ToString());
242        return tb;
243    }

244    /// <summary>
245    /// 通过考生姓名加载数据,模糊查询
246    /// </summary>
247    /// <param name="ksxm"></param>
248    /// <returns></returns>

249    public static kaoshengs.tksDataTable getbyxm(string ksxm)
250    {
251        //首先取出当前的权限
252        ArrayList alqx = quanxian.yanzheng();
253        kaoshengs.tksDataTable tb = new kaoshengs.tksDataTable();
254        kaoshengsTableAdapters.tksTableAdapter ksapt = new kaoshengsTableAdapters.tksTableAdapter();
255        tb = ksapt.GetDataByxm(ksxm, alqx[3].ToString(), alqx[4].ToString());
256        return tb;
257    }

258
259    /// <summary>
260    /// 通过考生号范围加载数据
261    /// </summary>
262    /// <param name="kshmin"></param>
263    /// <param name="kshmax"></param>
264    /// <returns></returns>

265    public static kaoshengs.tksDataTable getbyfw(string kshmin,string kshmax)
266    {
267        //首先取出当前的权限
268        //ArrayList alqx = quanxian.yanzheng();
269        ////将用户输入的范围置入当前权限允许的范围内
270        string min=kshmin,max=kshmax;
271        ArrayList alqx = quanxian.yanzheng();
272        if (Convert.ToInt64(kshmin) < Convert.ToInt64(alqx[3].ToString()))
273        { min = alqx[3].ToString(); }
274        if (Convert.ToInt64(kshmax) > Convert.ToInt64(alqx[4].ToString()))
275        { max = alqx[4].ToString(); }
276        kaoshengs.tksDataTable tb = new kaoshengs.tksDataTable();
277        kaoshengsTableAdapters.tksTableAdapter ksapt = new kaoshengsTableAdapters.tksTableAdapter();
278        tb = ksapt.GetDataByfw(min, max);
279        return tb;
280    }

281
282
283}

284
三、登录页面。页面如下,其中考生号有ajax效果控制不能输入除数字以外的信息

下边的该页的后台代码,为节省篇幅,仅给出进入系统按纽的代码如下:
需要注意的是因为引入了ajax所以输出提示信息的方式和以前不一样了.
 1 protected void btlogin_Click(object sender, EventArgs e)
 2    {
 3        ///取值
 4        string ksh = this.tbksh.Text.Trim();
 5        string code = this.tbcode.Text.Trim();
 6        string yanzheng = this.tbyanzheng.Text.Trim();
 7        ///判断验证码
 8        //提示弹出对话框需要注册客户端脚本
 9        ClientScriptManager csm = this.ClientScript;
10
11        if (tbyanzheng.Text.ToLower() == quanxian.yanzhengcode())
12        {
13            ///判断用户
14            ArrayList retal = kaosheng.yanzheng(ksh, code);
15            ///根据返回进行跳转
16            ///返回值为arrylist 其中
17            /// retal[0]存放int形式的返回值 
18            /// 其中 
19            /// 0:系统出错、数据库连接?
20            /// 1:已被录取
21            /// 2:登录正确
22            /// 3:首次登录需导向修改页面
23            /// 4:登录密码不对(已有但是不正确)
24            /// 5:未更改过密码,并且查分码不对

25            int backcode = Convert.ToInt16(retal[0].ToString().Trim());
26            if (backcode == 3 || backcode == 2)
27            {
28                string strip = Request.UserHostAddress;
29                kaosheng.loginlog(ksh, strip, backcode.ToString());
30
31            }

32            if (backcode == 0)
33            {
34                csm.RegisterStartupScript(this.GetType(), "Hello""<script>alert('服务器忙,或数据连接错,请联系管理员!')</script>");                    
35            }

36            if (backcode == 1)
37            {
38                csm.RegisterStartupScript(this.GetType(), "Hello""<script>alert('你已经被《" + retal[1].ToString() + "》录取,不能再填报其他志愿!')</script>");                   
39            }

40            if (backcode == 2)
41            {
42                //登录正确,首先准备参数数组,然后传递
43                retal[0= "0";//标记为0代表为考生信息
44                ///此时retal中存放
45                /// 标号     0       1       2          3           4           5           6           7
46                /// 包括:   0       考生号、考生姓名、 登录号      县区        地市        报名点  报名点代码

47               quanxian.buildsession(retal);
48                Response.Redirect("kszy.aspx");
49                
50            }

51            if (backcode == 3)
52            {
53                //首先准备,参数,包括 ksh,name、查分、县区、地市
54                ///此时retal中存放
55                /// 标号     0       1       2          3           4           5。。。
56                /// 包括:   3       考生号、考生姓名、 登录号      县区        地市。。。

57                
58                //调用静态方法将数据放入session中。
59                if(quanxian.buildsession(retal)) Response.Redirect("passchange.aspx");
60            }

61            if (backcode == 4)
62            {
63                csm.RegisterStartupScript(this.GetType(), "Hello""<script>alert('密码不正确,请联系管理员!')</script>");                   
64            }

65            if (backcode == 5)
66            {
67                csm.RegisterStartupScript(this.GetType(), "Hello""<script>alert('查分密码不正确,请联系管理员!')</script>");                    
68            }

69            if (backcode == 6)
70            {
71                csm.RegisterStartupScript(this.GetType(), "Hello""<script>alert('考生号输入不正确,本次操作已经被记录,请认真核对!')</script>");                    
72            }

73            ///1、导向修改密码
74            ///2、导向填报页面

75
76        }

77        else
78        {
79            //不相同则提示错误信息
80            csm.RegisterStartupScript(this.GetType(), "Hello""<script>alert('必须输入正确的验证码!')</script>");                
81        }

82    }

登录程序到这里就完成了.

posted on 2007-08-17 08:54  黄金年代  阅读(3035)  评论(17编辑  收藏  举报