当前位置: 首页 > 工具软件 > MapleFetion > 使用案例 >

飞信Client和MapleFetion项目的登录模块浅析

林承悦
2023-12-01

题记:

      反正闲着还是闲着。以下分析基于MapleFetion2.5版本。

     

    以前在DD的时候,XX做了一个告警短信系统,后来问了下孙总,他说只是XX提供了接口,应用这边给个文件就好了。所以此路XXX了。

 

    最早研究起Fetion的大哥应该是邓东东,记得之前面试我还提到过他的名字,他的LibFetion也是自己做黑盒分析做出来的,算是第三方最好的了,也做了很久,用c写的,见http://www.libfetion.org 原是本着美好的愿望,希望大家在此基础上开发更好的应用,无奈由于非技术原因,不提供对外的so库,特别是windows版本的,你懂的....

    虽然不对外提供so库,但却出了各个平台的飞信app应用了。国家要有个无私奉献奖就好了。

   

    听说飞信是MSN那帮人做的,而且做法有很多相似的地方。在2007年的时候nathan大哥也对其研究了一番,见他博客http://hi.baidu.com/nathan2007/home,他的博客要是被那帮开发人员看见了,估计会喷血。我想nathan的博客对后来研究飞信的人提供了很大的帮助。其中一个就是MapleFetion项目了,作者solosky也是做了一番苦心的研究,最终开发了一个功能较全的三方lib,并且开源出来了,我觉得这对飞信本身的发展是利大于弊的。

 

    根据nathan大哥的黑盒分析,了解到fetion用到非标准的SIP协议+HTTP协议,在SIP上做了一点改动,感觉是被阉了,并改名叫SIPC。

    飞信的登录模块有2次验证:

    1、SSI验证

    通过HTTPS的GET方式通讯,URL为:https://uid.fetion.com.cn/ssiportal/SSIAppSignInV2.aspx?mobileno=159********&domains=fetion.com.cn%3bm161.com.cn%3bwww.ikuwa.cn&digest=89405402A0FC1A2FC73681229FB134E9638C4D5986C65BC9

 

digest后面那段是密码。

 

得到SIP:123456@fetion.com.cn;p=6721,结果码,和一个Credential

类似如下:

 

<?xml version="1.0" encoding="utf-8" ?> 
- <results status-code="200">
- <user uri="sip:123456@fetion.com.cn;p=6721" mobile-no="159********" user-status="101" user-id="1233333">
- <credentials>
  <credential domain="fetion.com.cn" c="CBIOAADZDl2PfBl6ezl2Ljt9sWajwx2thKDwACt0dGTJ8gfb4C71CTLB7d8pxAD0BduMh7rO77pdWHIhHP2m0c7TRxQecfzOUCEfTyL09KaRQblUICPlBl/Whtp1M6WipxsyfX4AAA==" /> 
  <credential domain="m161.com.cn" c="CBAOAAAOPH9ouHxCxcsfS+XMPuAA8zN4nCaWV4t7mnHM6pkp6VHs1enfBCx/VT5lZ5lXU0WITAfctJ1f8wA4uUfyc1REihCQV/goRNumQC3VQinD1A==" /> 
  <credential domain="www.ikuwa.cn" c="ChAOAACTP8rQqlM7Sr3DbtWpRZv8HNrJpadWYWG0XWt3oNPWdSjx135Fk39Ghje1w4rdwZJ7eYtWX7/vv9lqD0uQzoio8CVuapOKZ1QcGa4fNJKaXQ==" /> 
  </credentials>
  </user>
  </results>

 

登录代码为:LoginState state = client.syncLogin();

登录主要类为:LoginWork.java

 

 LoginWork的工作包括了2次身份验证。完成了上一步,就开始SIPC的交互过程了,另外你可以不走SSI验证过程,但是你就必须得知道你的Fetion号,而非手机号。

 

SSI身份验证是类SSISignV2.java实现的,主要工作就是生成URL,发送HTTPS请求,接收HTTPS返回的结果,并解析,代码很简单,就不贴了。

SIPC验证是ServerDialog.java实现的,他主要工作是与飞信服务器进行通讯,其中包括消息的构造和发送。

 

在FetionClient初始化时会建立几个工厂:

 

public FetionClient(String account,
      String password,
      NotifyEventListener notifyEventListener)
 {
  this(new User(account, password, "fetion.com.cn"),
    notifyEventListener,
    new AutoTransferFactory(),
    new SimpleFetionStore(),
    new ThreadTimer(),
    new SingleExecutor());
 }

 

关于登录的运行有点怪怪的,作者用一个 new SingleExecutor()(实际上是一个1.5的单线程池:Executors.newSingleThreadExecutor();)来执行LoginWork。

 

 

2、开始SIPC验证

     前面分析到,与服务器的SIPC通讯主要是ServerDialog实现的,而他由DialogFactory工厂模式创建。

     和其他协议消息一样,消息分为头+消息体,由MessageFactory消息工厂创建,然后通过TCP,HTTP,MINA等底层通讯平台发送,消息被封装为SipcRequest.java,SipcResponse.java和SipcReceipt.java里。

 

    MessageFactory.java的主要作用就是构造一个个消息,消息构造就不贴代码了,很多硬编码了。 

 

    构造好消息后,传给上层ServerDialog.java,并由ServerDialog通过发送模块发送出去,并注册监听器。这个看得有点头昏,需要关注下,代码如下:

 

  /**
	 * 注册服务器
	 * @param presence		登录状态
	 * @param listener
	 */
	public void register(int presence, ActionEventListener listener)
	{
		SipcRequest request = this.getMessageFactory().createServerRegisterRequest(presence, 
					this.context.getTransferFactory().isMutiConnectionSupported());
		request.setResponseHandler(new ServerRegisterResponseHandler(context, this, listener));
		this.process(request);// 发送消息
		
	}

   

  /**
     * 发送数据包
     */
    @Override
    public void process(SipcOutMessage out)
    {
    	try {
	        this.processorChain.getFirst().processOutcoming(out);
        } catch (FetionException e) {
        	this.handleException(e);
        	//如果是传输异常,就将其抛出,让调用者处理传输错误
        	if(out instanceof SipcRequest) {
        		SipcRequest request = (SipcRequest) out;
        		if(request.getResponseHandler()!=null)
        			request.getResponseHandler().ioerror(request);
        	}
        }
    }

 

     看到发送消息通过process(request);从这个process看不出什么名堂。但是注意到在ServerDialog初始化的时候,初始化了这些process,如下:

 

 

    public void buildProcessorChain() throws FetionException
    {
		this.processorChain = new ProcessorChain();
		this.processorChain.addLast(new ServerMessageDispatcher(context, this, this));						//消息分发服务
		if(FetionConfig.getBoolean("log.sipc.enable"))
			this.processorChain.addLast(new SipcLogger("ServerDialog-"+this.context.getFetionUser().getFetionId()));									//日志记录
		this.processorChain.addLast(new TransferService(this.context));															//传输服务
		this.processorChain.addLast(new SipcParser());															//信令解析器
		this.processorChain.addLast(this.context.getTransferFactory().createDefaultTransfer());				//信令传输对象
		
		this.processorChain.startProcessorChain();
		
    }

其中 this.processorChain.addLast(new TransferService(this.context));建立一个传输processor放入processorChain这个以processor为节点的双链表。

 

我不太明白作者这样做的理由,看上去比较乱,processorChain里面什么都有,只能继续分析看看。

 

重新看了下各类Processor的处理逻辑后发现,原来作者的这个processorChain相当于一个过滤器,一个最原始的Request需要经过processorChain中每一个processor的处理,比如第一个是ServerMessageDispatcher模块,处理完后把Request对象传递给SipcLogger模块,从前往后,这样一层一层处理,最后由发送模块,也就是TCPTransfer的sendBytes进行发送。如下:

 

处理流程为:ServerMessageDispatcher-->SipcLogger-->TransferService-->SipcParser-->createDefaultTransfer-->网络-->飞信服务器

 

 protected void sendBytes(byte[] buff, int offset, int len)
            throws TransferException
    {
    	try {
	        this.writer.write(buff, offset, len);
	        this.writer.flush();
        } catch (IOException e) {
	       throw new TransferException(e);
        }
    	
    }

 

 这种把处理模块做成双链表的设计模式感觉还是有利有弊:),还是等待大大点评下吧。 

 

 类似资料: