BCHAT采用DISCUZ!NT论坛的外置验证方式实例
2008年10月26日BCHAT除了直接连接聊天室自身的数据库进行用户验证之外,还可以选择间接的MD5加密登录验证方法。这样可以让聊天室更好的和其他已有系统如论坛等相融合。其实质是:已有的其他用户验证系统独立进行用户身份验证,其验证过程与聊天室无关,用户验证通过以后再交给聊天室。
验证步骤如下:
步骤一、验证系统和聊天室双方约定登录验证密钥,如字符串:abcd;
步骤二、在聊天室配置文件中设置MD5KEY项的值为约定好的字符串,如:abcd;
步骤三、用户输入用户名和密码首先交给验证系统,验证系统访问外部数据库进行用户名密码验证,或者采用其他验证方式进行用户验证。该验证过程与聊天室无关。
步骤四、验证系统对通过验证的用户名如login123先进行“用户名+key”即“login123abcd”的MD5编码,然后跳转传递给聊天室端口的登录用户名参数,如http://host:port/?USER= login123&PASS= [MD5编码字符串],若需用到子房间,再加上参数&ROOMID=[子房间ID0-9]。
步骤五:聊天室检查所传过来的用户名和对应的MD5字符串是否由和验证系统双方约定的密钥产生的,如果是就让该用户进入聊天室。
聊天室的相关配置项:
MD5KEY=[如果采用MD5验证,则在这里设置约定的key]
MD5KEYLIMITTIME=[默认为0。为0时不作有效时间限制。为大于0的值时,则在该值指定的秒数内有效,作为密码的MD5值前面需要加上长度为10位的当前时间值以备比较,也就是:PASS=TIME+MD5(USER+TIME+KEY)]
如果用到影院看电影来比喻这个过程,那么就是:聊天室是播放大厅,验证系统是售票处。聊天室只认验证系统传过来的结果,就有如播放大厅只认售票处卖出去的门票。用户在验证系统买了门票以后,持票到播放大厅,播放大厅验证门票的正确与否,然后放用户进去。
下面就BCHAT与discuz!NT的登陆验证相结合做个实例说明。
discuz!NT可以配置用各种模板,默认的模板是在/config/general.config的“Templateid”配置项设置的。例如该项设置值为1,那么使用的默认模板就在/aspx/1目录下的。我们在/aspx/1目录下找个简单一点的文件来改动以生成聊天室的登陆入口文件,经查看发现其中的help.aspx结构比较简单,于是用它copy一个chat.aspx,然后把其中的help的内容掏掉,这样就具备了一个基本的具有登录验证功能的chat.aspx。在其中加上聊天室的房间列表和登录功能就可以了。加入的代码如下:
if (userid==-1) { templateBuilder.Append("<p><br>您尚未登录,请先进行登录。<br><br></p>\r\n"); } else { templateBuilder.Append("<style type=\"text/css\">\r\n"); templateBuilder.Append("#listdiv{ width:500px; margin:20px 20px 20px 20px; }\r\n"); templateBuilder.Append("#listdiv ul,li{ list-style:none; padding:0; }\r\n"); templateBuilder.Append("#listdiv li{ width:220px; float:left; margin:0px;padding:0px; line-height:25px}\r\n"); templateBuilder.Append("#listdiv p{color:Gray}\r\n"); templateBuilder.Append("#listdiv span{color:Gray}\r\n"); templateBuilder.Append("#listdiv .roomname{color:#886349}\r\n"); templateBuilder.Append("</style>\r\n"); templateBuilder.Append("<script language=\"JavaScript\" type=\"text/javascript\">\r\n"); templateBuilder.Append("var focusok=false;\r\n"); templateBuilder.Append("if (navigator.appName == \"Netscape\") {\r\n"); templateBuilder.Append(" focusok=true;\r\n"); templateBuilder.Append("}\r\n"); templateBuilder.Append("vers = navigator.appVersion;\r\n"); templateBuilder.Append("if (navigator.appName == \"Microsoft Internet Explorer\") {\r\n"); templateBuilder.Append(" pos = vers.lastIndexOf('.'); \r\n"); templateBuilder.Append(" vers = vers.substring(pos-1,vers.length); \r\n"); templateBuilder.Append("}\r\n"); templateBuilder.Append("proper_version = parseFloat(vers); \r\n"); templateBuilder.Append("\r\n"); templateBuilder.Append("if(proper_version>=5){\r\n"); templateBuilder.Append(" focusok=true;\r\n"); templateBuilder.Append("}\r\n"); templateBuilder.Append("\r\n"); templateBuilder.Append("var chat;\r\n"); templateBuilder.Append("function launchchat() {\r\n"); templateBuilder.Append("\r\n"); templateBuilder.Append(" chat = window.open(\"\",\"chat\",\"top=0,left=0,toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=790,height=530\");\r\n"); templateBuilder.Append(" if(focusok){\r\n"); templateBuilder.Append(" chat.focus();\r\n"); templateBuilder.Append(" }\r\n"); templateBuilder.Append(" return true;\r\n"); templateBuilder.Append("}\r\n"); templateBuilder.Append("function b(port,host)\r\n"); templateBuilder.Append("{\r\n"); templateBuilder.Append(" launchchat();\r\n"); templateBuilder.Append(" chat.document.URL=\"chatlogin.aspx?port=\"+port+\"&host=\"+host;\r\n"); templateBuilder.Append("} \r\n"); templateBuilder.Append("</"+"script>\r\n"); templateBuilder.Append("\r\n"); templateBuilder.Append("<div id=\"listdiv\">\r\n"); templateBuilder.Append("<p>请点击聊天室名称前面的小圈进入聊天室:\r\n"); templateBuilder.Append("</p>\r\n"); templateBuilder.Append("<ul> \r\n"); OdbcConnection myConnection = new OdbcConnection("Dsn=chatdb;dbq=C:\\bchat\\db\\chatdb.mdb;driverid=25;fil=MS Access;maxbuffersize=2048;pagetimeout=5;uid=admin;pwd=pass"); //OdbcConnection myConnection = new OdbcConnection("Dsn=chat;uid=chat;pwd=pass"); //如果是用MSSQL就用这行 string cmdText = "SELECT [port],[roomname],[num],[host] FROM [room] order by [port]"; OdbcCommand myCommand = new OdbcCommand(cmdText, myConnection); OdbcDataReader dr = null; try { myConnection.Open(); dr = myCommand.ExecuteReader(CommandBehavior.CloseConnection); while (dr.Read()) { templateBuilder.Append("<li>\r\n"); templateBuilder.Append("<input name=\"r\" onclick=\"if(this.checked){b('"+dr.GetString(0)+"','"+dr.GetString(3)+"')}\" type=\"radio\">\r\n"); templateBuilder.Append("<span class=\"roomname\">"+dr.GetString(1)+"</span><span>("+Convert.ToString(dr.GetInt32(2))+"人)</span>\r\n"); templateBuilder.Append("</li>\r\n"); } dr.Close(); } catch (OdbcException ex) { throw new Exception(ex.Message, ex); } templateBuilder.Append("</ul>\r\n"); templateBuilder.Append("</div>\r\n"); }
其中如果“userid==-1”就代表还没有登录论坛,提示用户先登录;否则就列出聊天室的房间列表,供用户选择登录。真正的登录操作则是通过chatlogin.aspx来完成。它从“username”变量提取已登录用户的用户名,然后做MD5KEY的运算,再提交给聊天室的入口。代码如下:
<%@ Page language="c#" AutoEventWireup="false" EnableViewState="false" Inherits="Discuz.Web.help" codepage=936 %> <%@ Import namespace="System.Data" %> <%@ Import namespace="Discuz.Common" %> <%@ Import namespace="Discuz.Forum" %> <%@ Import namespace="Discuz.Entity" %> <script runat="server"> public string host; public UInt32 port; public UInt32 roomid; public string name; public string pass; public string key; public UInt32 currenttime; override protected void OnInit(EventArgs e) { base.OnInit(e); if (userid==-1) { Response.Write("请先登陆."); Response.End(); return; } host = Request.Params["host"]; if (Request.Params["port"] != null) { port = Convert.ToUInt32(Request.Params["port"].ToString()); } else { port = 2000; } if(port>65535){ roomid=port %1000; port = Convert.ToUInt32(Math.Floor(port / 1000.0)); }else{ roomid=0; } name = username.ToString(); key = "md5key";//此为登陆程序与聊天室程序约定的md5key,必须和聊天室启动配置ini文件中定义的MD5KEY一致 DateTime dt = new DateTime(1970, 1, 1, 8, 0, 0); TimeSpan ts = DateTime.Now - dt; currenttime = Convert.ToUInt32(ts.TotalSeconds); pass = name + key; pass = MyMd5(pass); //如果设置了MD5KEYLIMITTIME则用下面的办法: //pass = name + currenttime.ToString() + key; //pass = MyMd5(pass); //pass = currenttime.ToString() + pass; } static string MyMd5(string str) { //byte[] b = System.Text.Encoding.Default.GetBytes(str); byte[] b = System.Text.Encoding.GetEncoding("gb2312").GetBytes(str); System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); byte[] d = md5.ComputeHash(b); string r = ""; for (int i = 0; i < d.Length; i++) r += d[i].ToString("x").PadLeft(2, '0'); return r; } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>聊天室的MD5KEY方式登陆</title> </head> <body> <form id="form1" runat="server"> <div> </div> </form> <form name="loginform" id="loginform" action="http://<%=host%>:<%=port%>/" method="post"> <input type="hidden" name="USER" value="<%=name%>" /> <input type="hidden" name="PASS" value="<%=pass%>" /> <input type="hidden" name="ROOMID" value="<%=roomid%>" /> 聊天室登陆中... </form> <script language="javascript" type="text/javascript"> document.loginform.submit(); </script> </body> </html>
注意这里把输出的页面编码设成了936,也就是gb2312,这是由于聊天室用的是gb2312编码。
完整的chat.aspx和chatlogin.aspx可以软件安装目录下的docs/md5logincodeexample/discuznt目录中找到。该目录下还有和其他论坛结合验证的例子。
如果在discuz!NT的aspx目录下有多个子目录,也就是用了多个模板,那么需要把chat.aspx和chatlogin.aspx两个文件复制到每个目录中去。