使用java编写SmartFoxServer自定义安全验证登录扩展

最近接触的东西有点杂,在写SmartFoxServer(以下简称SFS)服务端扩展时发现actionscript竟然只支持1.0,无奈只好用从来没有接触过的java来编写,参考官方cookbook及手册,成功实现了自定义安全验证登录扩展。
系统用的是CentOS5.2(x86_64),数据库是Oracle 11g,SFS版本为1.6.6。

  1. 配置

    数据库DEMO中的USERS表结构:

    CREATE TABLE "DEMO"."USERS" 
    (	
    "USERID" NUMBER(38,0) NOT NULL ENABLE, 
    "USERNAME" VARCHAR2(20) NOT NULL ENABLE, 
    "USERPWD" VARCHAR2(32) NOT NULL ENABLE, 
    "USEREMAIL" VARCHAR2(100), 
     CONSTRAINT "TABLE1_PK" PRIMARY KEY ("USERID") ENABLE
    ) ;

    将连接java连接oracle的驱动(oracle/product/11.1.0/db_1/jdbc/lib/ojdbc6.jar,这里我用的是JDK1.6,所以用ojdbc6.jar,根据自己的需要复制驱动)复制到(SFS_PRO_1.6.6/jre/lib/ext/)下,然后修改SFS根目录下的config.xml,在Zones节点内加入如下Zone配置:

    <zone name="Demo" uCountUpdate="true" buddyList="20" maxUsers="4000" customLogin="true">
    	<rooms>
    		<room name="The Hall" maxUsers="50" isPrivate="false" isTemp="false" autoJoin="true" uCountUpdate="true" />
    	</rooms>
     
    	<extensions>
    		<extension name="demo_login"  className="demo_login" type="java" />
    	</extensions>
     
    	<databasemanager active="true">
     
    		<driver>oracle.jdbc.driver.OracleDriver</driver>
    		<connectionstring>jdbc:oracle:thin:@localhost:1521:DEMO</connectionstring>				
     
    		<username>demo</username>
    		<password>demo</password>
     
    		<testsql>< ![CDATA[SELECT COUNT(*) FROM USERS]]></testsql>
     
    		<maxactive>10</maxactive>
    		<maxidle>10</maxidle>
     
    		<onexhaustedpool>fail</onexhaustedpool>
    		<blocktime>5000</blocktime>
     
    	</databasemanager>
     
    </zone>

    这里根据自己数据库配置来设置驱动、连接字符串、用户名、密码,如果用的是mysql,那么驱动和连接字符串应该如下:

    <driver>com.mysql.jdbc.Driver</driver>
    <connectionstring>jdbc:mysql://localhost:3306/DEMO</connectionstring>
  2. 编写扩展

    在写扩展之前,需要了解一下安全验证的机制,借用一下官方的图示:
    secureLoginDiagram
    第一步:从服务器获取随机码,把用户输入密码用md5加密,然后再把随机码和加密后的md5码再用md5加密,并发送给服务器。
    第二步:服务器端从数据库取出经过md5加密的用户密码,同样用md5再加密一次随机码和取出的md5密码,然后和客户端发来的hash过的字符进行对比,来验证登录。

    理解了这次,就可以开始编写Zone配置里的扩展demo_login了,关于扩展的具体说明,看官方手册就可以了,这里给出源码借参考:

    import java.nio.channels.SocketChannel;
    import java.util.*;
     
    import it.gotoandplay.smartfoxserver.crypto.MD5;
    import it.gotoandplay.smartfoxserver.data.*;
    import it.gotoandplay.smartfoxserver.db.*;
    import it.gotoandplay.smartfoxserver.exceptions.*;
    import it.gotoandplay.smartfoxserver.extensions.*;
    import it.gotoandplay.smartfoxserver.lib.ActionscriptObject;
    import it.gotoandplay.smartfoxserver.events.InternalEventObject;
     
     
    public class demo_login extends AbstractExtension
    {
    	private ExtensionHelper helper;
    	private Zone currentZone;
    	private DbManager db;
     
    	public void init()
    	{
    		helper = ExtensionHelper.instance();
    		this.currentZone = helper.getZone(this.getOwnerZone());
    	}
     
    	public void destroy()
    	{
    		trace("Extension destroyed");
    	}
     
    	public void handleRequest(String cmd, ActionscriptObject ao, User u, int fromRoom)
    	{
    		// Your code here
    	}
     
    	public void handleRequest(String cmd, String params[], User u, int fromRoom)
    	{
    		// Your code here
    	}
     
    	/**
    	 * Handle Internal Server Events
    	 * 
    	 * @param ieo		the event object
    	 */
    	public void handleInternalEvent(InternalEventObject ieo)
    	{
    		if (ieo.getEventName().equals("loginRequest"))
    		{
    			// 根据配置文件里的DatabaseManager配置获得当前Zone的数据库操作对象
    			db = this.currentZone.dbManager;
    			ActionscriptObject response = new ActionscriptObject();
    			User loginUser = null;
     
    			// 获取用户名
    			String nick = ieo.getParam("nick");
    			// 获取客户端利用服务器加密字符串和用户密码加密后MD5密码
    			String ClientPassword = ieo.getParam("pass");
    			// 获取服务器socket通道
    			SocketChannel chan = (SocketChannel)ieo.getObject("chan");
    			// 根据socket通道生成加密字符
    			String ServerRandom = helper.getSecretKey(chan);
    			// 获取数据库中用户的密码
    			ArrayList arrList = db.executeQuery("SELECT USERPWD FROM USERS WHERE USERNAME='"+nick+"'");
    			String ServerPassword = "";
    			boolean IsLogin = false;
    			if (arrList.size() < = 0)
    			{
    				response.put("_cmd", "loginKO");
    				response.put("err", "用户名不存在,登录失败。");
    			}
    			else
    			{
    				// 获取数据库密码
    				DataRow dr = arrList.get(0);
    				String dbpass = dr.getItem("USERPWD");
    				// 根据服务器加密字符和数据库用户密码,生成服务器端的混合MD5密码
    				ServerPassword = MD5.instance().getHash(ServerRandom + dbpass);
    				IsLogin = (ClientPassword.equals(ServerPassword)) ? true : false;
    				if (IsLogin)
    				{
    					try
    					{
    						// 登录成功
    						loginUser = helper.canLogin(nick, ClientPassword, chan, this.currentZone.getName());
    						response.put("_cmd", "loginOK");
    						response.put("id", String.valueOf(loginUser.getUserId()));
    						response.put("name", loginUser.getName());
    					}
    					catch (LoginException e)
    					{
    						// 登录失败
    						response.put("_cmd", "loginKO");
    						response.put("err", e.getMessage());
    					}
    				}
    				else
    				{
    					response.put("_cmd", "loginKO");
    					response.put("err", "认证失败.");
    				}
    			}
     
    			LinkedList linkedlist = new LinkedList();
    			linkedlist.add(chan);
    			// 返回结果给客户端
    			this.sendResponse(response, -1, null, linkedlist);
    			if (IsLogin)
    				helper.sendRoomList(chan);
    		}
    	}
    }

    这里有个关键点需要注意:配置文件中Zone标签的属性customLogin要设置成true才能使服务端响应loginRequest事件。

  3. 客户端处理

    服务端的验证结果信息将通过客户端SmartFoxClient对象的onExtensionResponse返回,下面是该事件监听函数:

    private function onExtensionResponseHandler(e:SFSEvent):void
    {
    	// 数据传输类型
    	var type:String = e.params.type;
    	// 用Object类型接收数据
    	var data:Object = e.params.dataObj;
    	var cmd:String = data._cmd;
    	switch (cmd)
    	{
    		case "loginKO":
    			this._labWelcome.setText(data.err);
    			break;
    		case "loginOK":
    			_SFSClient.myUserId = data.id;
    			_SFSClient.myUserName = data.name;
    			initResources();
    			break;
    		default:
    			this._labWelcome.setText(data.err);
    			break;
    	}
    }

你可能还对下面的日志感兴趣:

相关标签: Development & Design and tagged , , , .

4 Responses to 使用java编写SmartFoxServer自定义安全验证登录扩展

  1. Son says:

    response.put(“err”, “认证失败.”);
    为什么中文传到客户端是显示???问号的?这个可以解决吗?
    可以的话发到我邮箱,谢谢

    • 唯枫 says:

      我的是正常显示的,没有乱码
      你的开发工具如果是flex builder的话,可以看看选项-》常规-》工作区 里的文本文件编码是不是UTF-8,不是的话改一下试试。

  2. 吕云飞 says:

    你好。。我用你的java代码试了下。。配置文件也改了。。可是服务器启动时却停在那儿。。显示wrong name:demo_login/demo_login 这是为啥呢?

  3. 吕云飞 says:

    弄好了。。原来不要建package就好了。555

发表评论

电子邮件地址不会被公开。 必填项已被标记为 *

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>