一步一步asp.net_ajax_用户信息管理
前两天,一直忙着维护一个以前的项目,凌乱的代码,看到就想全部重构一边,又怕引起新的问题,一直折腾了好多天,几天都没总结,写博客了.
在网站中经常性碰到的一个问题就是关于用户信息中地址的省市县三级联动设计,特别是在电子商务中,经常性遇到,今天,我们继续设计,关于用户信息的设计以及三级联动.
任务:
用户信息管理和三级联动设计.
先说点题外话:
因为用户信息模块不需要搜索引擎收录,我们可以大量使用ajax设计,然后最后全部生成html的方式,将会给网站带来非常高的性能,用户信息管理这些模块全部都可以用ajax,最后生成html,之前,有2个大师列表页和企业列表页用的ajax做的分页, 这不利于搜索引擎SEO,后来全部改成动态的了,为了性能而影响搜索引擎的SEO,对于电子商务网站可能就是致命的打击.
前几篇日志中有一篇就涉及到ajax分页的问题,一般而言,我感觉内容页,比如新闻内容,工艺知识,首页内容展示,可以用分页设计,动态的部分ajax做,至于列表页,则不能使用ajax,不然影响SEO,个人空间,用户管理则全部使用ajax做比较好,最后可以全部生成html页面的方式配合ajax缓解服务器压力.
进入正题:
主要实现界面:
大致就是用户信息读取,三级联动,
难度倒是没大多,主要是三级联动的另类设计,
在博客园参考了一个博客友人的数据库设计.
以前设计的是这样子的,
这样的方式分散式设计挺不好的,浪费空间,博客园有一位仁兄,这么设计,
通过树形菜单的形式,这样我感觉非常好,可以简化好多操作,查询,就直接根据parentid,搜索下一级,
主要的js联动设计:(代码已经做了详细的注释,相信不用说也大致知道怎么做了,哈哈)
1:
2: $(function(){
3:
4: //省市数据初始化
5: GetProvince();
6: //获取民族信息
7: GetNation();
8: //绑定事件
9: $("#selProvince").change(function(){
10: GetCity();
11: });
12: $("#selCity").change(function(){
13: GetCountry();
14: });
15: GetUserInfo();
16:
17: });
18:
19: //提交用户信息
20: function Comit(){
21: //如果验证不通过,就不提交
22: if(!$("#form1").validationEngine('validate'))
23: return;
24: $("#btnUpdate").click(function(){
25: $.ajax({
26: url:"Data/GetMemberInfo.ashx?method=UpdateUserInfo",
27: data:{"UserName":$("#txtUserName").val(),"QQ":$("#txtQQ").val(),"Email":$("#txtEmail").val(),"Nation":$("#selNational").val(),"Sex":$("#Man").val(),"Province":$("#selProvince").val(),"City":$("#selCity").val(),"Country":$("#selCountry").val(),"Address":$("#txtHomeBase").val(),"ZipCode":$("#txtZipCode").val(),"MobilePhone":$("#txtMobilePhone").val(),"TelPhone":$("#txtTelPhone").val()},
28: type:"post",
29: success:function(text){
30: var DataJson=$.parseJSON(text);
31: if(DataJson.Status){
32: alert('保存成功!');
33: }
34: }
35: });
36: });
37: }
38: //获取民族信息
39: function GetNation(){
40: $.ajax({
41: url:"../Admin/CommonLibs/national.txt",
42: dataType:"text",
43: success:function(text){
44: //不安全的用法
45: var DataJson=eval("("+text+")");
46:
47: var item='';
48: $.each(DataJson,function(key,value){
49: item+='<option value="'+value.id+'">'+value.text+'</option>';
50: })
51: $("#selNational").empty();
52: $("#selNational").append(item);
53: }
54:
55: });
56: }
57: //获取用户信息
58: function GetUserInfo(){
59: $.ajax({
60: url:"Data/GetMemberInfo.ashx?method=GetUserInfo",
61: type:"post",
62: success:function(text){
63: var DataJson=$.parseJSON(text);
64: if(DataJson.Status){
65: $("#txtUserName").val(DataJson.Data[0].UserName);
66:
67: $("#selNational").val(DataJson.Data[0].Nation);
68: $("#txtQQ").val(DataJson.Data[0].QQ);
69: $("#txtEmail").val(DataJson.Data[0].Email);
70: if(DataJson.Data[0].Sex=='1')
71: $("#Man").val(DataJson.Data[0].Sex);
72: else
73: $("#Woman").val('1');
74: //给省市县赋值
75: $("#selProvince").val(DataJson.Data[0].Province);
76: GetArea($("#selProvince").val(),"#selCity",DataJson.Data[0].City);
77: GetArea( DataJson.Data[0].City,"#selCountry",DataJson.Data[0].Country);
78: $("#txtHomeBase").val(DataJson.Data[0].Address);
79: $("#txtZipCode").val(DataJson.Data[0].ZipCode);
80: $("#txtMobilePhone").val(DataJson.Data[0].MobilePhone);
81: $("#txtTelPhone").val(DataJson.Data[0].TelePhone);
82:
83: }
84:
85: }
86: });
87:
88: }
89: //更改邮箱事件
90: function ChangeEmail(){
91:
92: $("#txtEmail").attr("disabled",false);
93:
94: }
95: //获取省级数据
96: function GetProvince(){
97: //首先省市数据初始化(获取省级CodeId=0
98: $("#selProvince").empty();
99: // $("#selProvince").append("<option value='-1'>请选择</option>");
100: GetArea(0,"#selProvince");
101: //清空市级数据
102: $("#selCity").empty();
103: // $("#selCity").append("<option value='-1'>请选择</option>");
104: //清空县级数据
105: $("#selCountry").empty();
106: // $("#selCountry").append("<option value='-1'>请选择</option>");
107:
108: }
109: //联动市级
110: function GetCity(){
111: if($("#selProvince").val()=='null'){
112: return;
113: }
114: else{
115: $("#selCity").empty();
116: // $("#selCity").append("<option value='-1'>请选择</option>");
117: GetArea($("#selProvince").val(),"#selCity",'');
118:
119: }
120: }
121: //联动县级
122: function GetCountry(){
123: if($("#selCity").val()=='null'){
124: return;
125: }
126: else{
127: $("#selCountry").empty();
128: // $("#selCountry").append("<option value='-1'>请选择</option>");
129: GetArea($("#selCity").val(),"#selCountry",'');
130:
131: }
132: }
133: //联动获取json数据
134: function GetArea(CodeId,e,InitData){
135: $.ajax({
136: url:"Data/GetMemberInfo.ashx?method=GetArea&CodeId="+CodeId,
137: type:"post",
138: success:function(text){
139: var DataJson=$.parseJSON(text);
140: var item='';
141: if(DataJson.Status){
142: $.each(DataJson.Data,function(key,value){
143: item+='<option value="'+value.CodeId+'">'+value.CityName+'</option>';
144: });
145:
146: }
147:
148: $(e).append(item);
149: //赋初值
150: if(InitData!=''){
151: $(e).val(InitData);
152: }
153: }
154: });
155: }
后台的BLL读取:1: /// <summary>
2: /// 根据父级id获取下级
3: /// </summary>
4: /// <param name="id">父级id</param>
5: /// <returns></returns>
6: public string GetAreaByJson(string id)
7: {
8: //查询状态
9: bool Status = false;
10: //根据父级id获取下级
11: IEnumerable<dbProvince> list = new dbProvinceDAL().GetArea(id);
12: //转化为json格式
13: StringBuilder json = new StringBuilder();
14: StringWriter sw = new StringWriter(json);
15: using (JsonWriter jsonWriter = new JsonTextWriter(sw))
16: {
17:
18: jsonWriter.Formatting = Formatting.Indented;
19: //判断数据读取状态
20: if (list.Count() > 0)
21: {
22: Status = true;
23: }
24: jsonWriter.WriteStartObject();
25: jsonWriter.WritePropertyName("Status");
26: jsonWriter.WriteValue(Status);
27: jsonWriter.WritePropertyName("Data");
28:
29: jsonWriter.WriteStartArray();
30: if (Status == true)
31: {
32: foreach (dbProvince dbInfo in list)
33: {
34: jsonWriter.WriteStartObject();
35: jsonWriter.WritePropertyName("CodeId");
36: jsonWriter.WriteValue(dbInfo.codeid);
37: jsonWriter.WritePropertyName("CityName");
38: jsonWriter.WriteValue(dbInfo.cityName);
39: jsonWriter.WriteEndObject();
40: }
41: }
42: jsonWriter.WriteEndArray();
43: jsonWriter.WriteEndObject();
44:
45: }
46: return json.ToString();
47: }
这样一个简单的三级联动就设计完了,感觉三级联动是学ajax最基础,也比较容易设计的,多尝试,这里只是感觉这种思路很好,一张表就能解决问题.
接下来,我们开始设计用户信息管理,主要是一些安全性问题,特别是比如修改密码的安全性,找回密码,注册帐号的邮箱验证安全性.
首先是帐号注册:
帐号注册,我们之前就做过了,但是还有一些问题,比如邮箱验证的安全性设计,
当用户注册账号以后,我们要提示用户,到xxx邮箱激活帐号,我们就要在数据库设计2个字段,一
个是过期时间,一个是验证码,因为为了防止破解,我们需要这样设计保证安全.
注册的部分上次涉及到了,这里就主要贴出注册的主要c#后台代码:
1: /// <summary>
2: /// 保存用户信息
3: /// </summary>
4: /// <param name="context"></param>
5: public void SaveMemberInfo(HttpContext context)
6: {
7: memberBLL bll = new memberBLL();
8: try
9: {
10: //表单读取
11: string txtUserName = context.Request["txtUserName"];
12: string txtPwd = context.Request["txtPwd"];
13: string txtEmail = context.Request["txtEmail"];
14: string txtCheckCode = context.Request["txtCheckCode"];
15: //验证码校验
16: if (!txtCheckCode.Equals(context.Session["checkcode"].ToString()))
17: {
18: context.Response.Write(bll.WriteJsonForReturn(false, ""));
19: }
20: //字符串sql注入检测
21: if (Tools.IsValidInput(ref txtUserName, true) && Tools.IsValidInput(ref txtPwd, true) && Tools.IsValidInput(ref txtEmail, true))
22: {
23: member info = new member();
24: info.username = txtUserName;
25: info.password =Tools.GetMD5(txtPwd);
26: info.Email = txtEmail;
27: info.states = "0";
28:
29: //加随机验证码
30: info.VCode = Guid.NewGuid().ToString("N");
31: //验证失效(1小时以内激活有效)
32: info.VTime = DateTime.Now.AddHours(1);
33:
34: //验证用户名
35: if (!bll.CheckExistUserName(info.username)) {
36: context.Response.Write(bll.WriteJsonForReturn(false, ""));
37: return;
38: }
39: if (bll.AddNew(info) > 0)
40: {
41: SMTP smtp = new SMTP(info.Email);
42: //激活网址生成
43: string webpath = context.Request.Url.Scheme + "://" + context.Request.Url.Authority + System.Web.VirtualPathUtility.ToAbsolute("~/Member/EmailChecking.aspx")+"?UserName="+context.Server.UrlEncode(info.username)+"&YZM="+info.VCode;
44: //发送激活邮件
45: if (smtp.Activation(webpath, info.username))
46: {
47:
48: context.Response.Write(bll.WriteJsonForReturn(true, Tools.GetEmail(info.Email)));
49:
50: }
51: else {
52: context.Response.Write(bll.WriteJsonForReturn(false, ""));
53: }
54: }
55: else
56: {
57: context.Response.Write(bll.WriteJsonForReturn(false, ""));
58: }
59: }
60: }
61: catch (Exception ex)
62: {
63: logger.Error("错误!", ex);
64: context.Response.Write(bll.WriteJsonForReturn(false, ""));
65: }
66: }
我们在生成随机码的时候,可以采用Guid,全球唯一标识,而且加上过期时间1个小时,而且注意给用户的邮箱提示内容要做安全性隐藏修改:
这样可以防止邮箱被人知道,这样子,邮箱在现在是一个非常重要的存在.这一点细节还是要注意的,
1: /// <summary>
2: /// //前台显示邮箱的掩码替换(由tzh@qq.com等替换成t*****@qq.com)
3: /// </summary>
4: /// <param name="Email">邮箱</param>
5: /// <returns></returns>
6: public static string GetEmail(string Email)
7: {
8:
9: string strArg = "";
10: string SendEmail = "";
11: Match match = Regex.Match(Email, @"(\w)\w+@");
12:
13: if (match.Success)
14: {
15: strArg = match.Groups[1].Value + "*****@";
16: SendEmail = Regex.Replace(Email, @"\w+@", strArg);
17: }
18: else
19: SendEmail = Email;
20: return SendEmail;
21: }
效果如下:
哈哈哈,这样子是不是友好多了,我们再次查看代码
接下来,我们要设计一个邮箱激活的设计,
邮箱激活,记得必须到数据库检查随机码是否正确,并且用户名的正确性, 以及是否过期,
BLL设计:
1: /// <summary>
2: /// 验证用户信息
3: /// </summary>
4: /// <param name="UserName">用户名</param>
5: /// <param name="GuidInfo">guid随机码</param>
6: public bool ActivationMemberNumber(string UserName, string GuidInfo)
7: {
8: memberDAL dal = new memberDAL();
9: //获取过期时间
10: DateTime dt = dal.GetMemberVTime(UserName, GuidInfo);
11: //如果已经过期
12: if (dt < DateTime.Now)
13: {
14: return false;
15: }
16: else
17: {
18: //激活帐号
19: return dal.ActivationMemberStatus(UserName);
20: }
21: }
ashx中的处理方法代码:
1: /// <summary>
2: /// 激活帐号
3: /// </summary>
4: /// <param name="context"></param>
5: public void ActivationMemberNumber(HttpContext context)
6: {
7: //注意要对url解码
8: string UserName = context.Server.UrlDecode(context.Request["UserName"]);
9:
10: string VCode = context.Request["VCode"];
11: memberBLL bll = new memberBLL();
12: //激活
13: if (Tools.IsValidInput(ref UserName, true) && Tools.IsValidInput(ref VCode, true))
14: {
15: context.Response.Write(bll.WriteJsonForReturn(bll.ActivationMemberNumber(UserName, VCode), "")); ;
16: }
17: else
18: {
19: context.Response.Write(bll.WriteJsonForReturn(false, ""));
20: }
21:
22:
23: }
效果图:
接下来,我们设计找回密码的功能:
找回密码是几乎所有的网站都有的功能,但是非常重要的一点就是安全性,网站本身也不大,我
就直接通过邮箱发送重置密码给用户,密码采用MD5加密,我们验证用户名和邮箱是否正确,如
果正确,我们就生成一个6位的随即密码修改用户密码,然后给用户发邮件,并告诉用户修改的随
即密码,通知用户修改密码.
前台代码(验证组件采用的JQuery Validate):
1: <%@ Page Language="C#" MasterPageFile="~/Member.master" AutoEventWireup="true" CodeFile="ChangeSercret.aspx.cs" Inherits="Member_ChangeSercret" Title="修改密码" %>
2:
3: <asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
4: <link href="../Admin/css/template.css" rel="stylesheet" type="text/css" />
5: <link href="../Admin/css/validationEngine.jquery.css" rel="stylesheet" type="text/css" />
6: <script src="../js/jquery.validationEngine.js" type="text/javascript"></script>
7: <script src="../js/languages/jquery.validationEngine-zh_CN.js" type="text/javascript"></script>
8: <script type="text/javascript">
9: $(function(){
10: $("#form1").validationEngine();
11: $("#btnUpdate").click(function(){
12: //如果验证不通过,就不提交
13: if(!$("#form1").validationEngine('validate'))
14: return;
15: $.ajax({
16: url:"Data/GetMemberInfo.ashx?method=UpdatePwd",
17: data:{"txtOldPwd":$("#txtOldPwd").val(),"txtNewPwd":$("#txtNewPwd").val() },
18: type:"post",
19: success:function(text){
20: var dataJson=$.parseJSON(text);
21: if(dataJson.Status){
22: alert("修改成功!");
23: }
24: else if(!dataJson.Status&&dataJson.Data==''){
25: alert("未登录!");
26: window.location.replace("MemberLogin.aspx");
27: }
28: else{
29: alert("修改失败!");
30: }
31: }
32:
33: });
34: });
35: });
36:
37:
38:
39:
40: </script>
41: </asp:Content>
42: <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
43: <form id="form1">
44: <div class="zz">
45:
46: <div class="xgmm">
47:
48: <div class="xgmm_tt"><h3>修改密码</h3></div>
49:
50: <p class="xgmm_tip">密码由6-15个字符组成,为了您账号的安全,禁止使用全数字或连续字符作为密码。</p>
51:
52: <ul class="xgmm_ul">
53:
54: <li class="xgmm_ul_li1"><span>当前密码:</span><input type="text" class="xgmm_text validate[required,custom[LoginPwd]]" name="txtOldPwd" id="txtOldPwd"/> </li><%----%>
55:
56:
57: <li><span>新密码:</span><input type="text" class="xgmm_text validate[required,custom[LoginPwd]]" id="txtNewPwd"/></li>
58:
59: <li><span>确认密码:</span><input type="text" class="xgmm_text validate[required,equals[txtNewPwd]]" id="txtConfirmPwd" /></li>
60:
61: <li class="xgmm_bt"><input type="button" class="xgmm_bt1" id="btnUpdate" /><input type="reset" value="" class="xgmm_bt2" /></li>
62:
63: </ul>
64:
65: </div>
66:
67: </div>
68: </form>
69:
70: </asp:Content>
71:
后台的方法:
1: /// <summary>
2: /// 忘记密码
3: /// </summary>
4: /// <param name="context"></param>
5: public void ForgetPwd(HttpContext context)
6: {
7: memberBLL bll = new memberBLL();
8: string UserName = context.Request["UserName"];
9: string CheckCode = context.Request["CheckCode"];
10: string Email = context.Request["Email"];
11: //验证码校验
12: if (!CheckCode.Equals(context.Session["checkcode"].ToString ()))
13: {
14: context.Response.Write(bll.WriteJsonForReturn(false, "验证码不正确!"));
15: return;
16: }
17: //字符串sql注入检测
18: if (Tools.IsValidInput(ref UserName, true) && Tools.IsValidInput(ref Email, true))
19: {
20: //获取用户邮箱
21:
22: //邮箱和用户名状态(正确与否)
23: bool status = bll.CheckUserNameAndEmail(UserName,Email);
24: //随机生成一个6位的新密码
25: string NewPwd = bll.CreateNewPwd();
26: if (!string.IsNullOrEmpty(Email) && status && bll.UpdatePwd(UserName, NewPwd))
27: {
28: SMTP smtp = new SMTP(Email);
29: if (smtp.sendemail("潮州工艺品平台", "尊敬的" + UserName + "用户:恭喜您,您在" + DateTime.Now.ToString() + "使用找回密码功能重置密码,您的密码:" + NewPwd + ",请尽快修改密码并妥善保管!"))
30: {
31: context.Response.Write(bll.WriteJsonForReturn(true, Tools.GetEmail(Email)));
32: }
33: else {
34: context.Response.Write(bll.WriteJsonForReturn(false , "邮箱发送失败!"));
35: }
36: }
37: else
38: {
39: context.Response.Write(bll.WriteJsonForReturn(false, "用户名或邮箱不正确!"));
40: }
41:
42:
43: }
44: else
45: {
46: context.Response.Write(bll.WriteJsonForReturn(false, "输入非法内容!"));
47: }
48: }
BLL中的主要方法设计:
1: /// <summary>
2: /// 检查用户名和邮箱
3: /// </summary>
4: /// <param name="UserName">用户名</param>
5: /// <param name="Email">邮箱</param>
6: /// <returns></returns>
7: public bool CheckUserNameAndEmail(string UserName,string Email)
8: {
9: memberDAL dal=new memberDAL ();
10: member info = dal.GetMemberInfo(UserName);
11: return Email==info.Email;
12: }
13: /// <summary>
14: /// 随机生成一个6位的密码
15: /// </summary>
16: /// <returns></returns>
17: public string CreateNewPwd()
18: {
19: string Pwd="";
20: Random ran=new Random (DateTime.Now.Second);
21: for (int i = 0; i < 6; i++)
22: {
23: Pwd += ran.Next(1, 10);
24: }
25: return Pwd;
26: }
27: /// <summary>
28: /// 修改密码
29: /// </summary>
30: /// <param name="UserName">用户名</param>
31: /// <param name="Pwd">密码</param>
32: /// <returns></returns>
33: public bool UpdatePwd(string UserName, string Pwd)
34: {
35: return new memberDAL().UpdatePassword(UserName, Tools.GetMD5(Pwd));
36: }
效果图:
本来接下来准备做一下,ajax实现购物车的设计,不过挺麻烦的,难做,正在纠结中...................