MyCat源码解读(1)Auth协议处理

MyCat如何处理认证Auth协议

 
 

MyCat Server的前世今生本人就不做阐述了,能看到这篇文章的人,应该已经了解了。

 

作为Mysql的中间件,Mycat有两种连接,一种是让用户看起来是用Mysql的,处理Mysql请求的前端连接FrontendConnection。另外一种,是实际与后端Mysql物理库交换数据的后端连接BackendConnection。

前端连接,是用户与mycat沟通的桥梁。

 

我使用的客户端是mysql.exe

不管你是用Mysql的图形界面工具,还是用Mysql的命令行工具。如mysql.exe,在进行mysql -u username -ppassword -Pport -hHost的时候。第一步要做的就是给Mysql或者Mycat发送connect请求。

 

Mycat处理Auth请求和Mysql处理请求的过程并没有什么区别。本人仅就Mycat处理的相关过程,即从代码角度做简单描述。

 

传统的Mysql认证协议过程如下:

 

Client               Server
  |      handshake     |
  |<-------------------|
  |   authentication   |
  |------------------->|
  |     auth result    |
  |<-------------------|
  |                    |



Mycat模拟的就是一个MysqlServer,故而与Mysql的协议过程相同

 

 

 

public void register(Selector selector) throws IOException {
		super.register(selector);
		if (!isClosed.get()) {
			// 生成认证数据
			byte[] rand1 = RandomUtil.randomBytes(8);
			byte[] rand2 = RandomUtil.randomBytes(12);

			// 保存认证数据
			byte[] seed = new byte[rand1.length + rand2.length];
			System.arraycopy(rand1, 0, seed, 0, rand1.length);
			System.arraycopy(rand2, 0, seed, rand1.length, rand2.length);
			this.seed = seed;

			// 发送握手数据包
			HandshakePacket hs = new HandshakePacket();
			hs.packetId = 0;
			hs.protocolVersion = Versions.PROTOCOL_VERSION;
			hs.serverVersion = Versions.SERVER_VERSION;
			hs.threadId = id;
			hs.seed = rand1;
			hs.serverCapabilities = getServerCapabilities();
			hs.serverCharsetIndex = (byte) (charsetIndex & 0xff);
			hs.serverStatus = 2;
			hs.restOfScrambleBuff = rand2;
			hs.write(this);
		}

上面代码片段,是用户发出连接请求后,MyCatServer accept连接,立即发出的握手包。

 

这里插一句,MycatServer的Acceptor,使用NIO框架,在每一个accept的请求到来后,立即注册其到Read事件中。

前端连接FrontedConnection继承了父类register的同时,还将握手包送发出去了。

 

 

MyCatserver给客户端发送的HandshakePacket中,包含了

packetId:包的ID

threadid:发送此包的线程id

seed:加密数据用的byte[]的前半部分

serverCapabilities:服务端属性

serverCharsetIndex:服务端字符集

serverStatus:服务端状态

restOfScrambleBuff:加密数据用的byte[]后半部分

 

客户端收到握手包之后,会将用户名,以及用seed加密后的密码,还有字符集,数据库等等连接参数发送一个认证包过来。就在mysql -u username -ppassword -Pport -hHost执行命令并在Tcp连接建立后。

 

在MyCat中,处理这个认证数据包AuthPacket的是FrontendConnection,FrontendConnection在构造的时候首先绑定了一个数据处理的Handler--FrontendAuthenticator

 

public FrontendConnection(SocketChannel channel) {
		super(channel);
		Socket socket = channel.socket();
		this.host = socket.getInetAddress().getHostAddress();
		this.port = socket.getPort();
		this.localPort = socket.getLocalPort();
		this.handler = new FrontendAuthenticator(this);
	}

 

当客户端发送Auth认证信息过来后,MyCat通过FrontendConnection取得数据,并将数据异步交给handler处理。

 

		if (data[4] == MySQLPacket.COM_QUIT) {
			this.getProcessor().getCommands().doQuit();
			this.close("quit cmd");
			return;

		}
		// 异步处理前端数据
		// processor.getHandler().execute(new Runnable()
		processor.getExecutor().execute(new Runnable() {
			@Override
			public void run() {
				try {
					handler.handle(data);
				} catch (Throwable t) {
					error(ErrorCode.ERR_HANDLE_DATA, t);
				}
			}
		});
	

此时,刚建立连接的FrontendConnetion持有的是FrontendAuthenticator的实例handler

 

handler首先用户密码的正确性。查找当前配置的用户表中有误此用户

checkUser(String user, String host)

然后,用发送给客户端的seed做了密码加密,并和客户端的进行比对:

encryptPass = SecurityUtil.scramble411(pass.getBytes(), source.getSeed());

然后检查数据库是否存在checkSchema(String database, String user)

当一切检查完毕后,向客户端发送Ok数据包。

 

protected void success(AuthPacket auth) {
        source.setAuthenticated(true);
        source.setUser(auth.user);
        source.setSchema(auth.database);
        source.setCharsetIndex(auth.charsetIndex);
        source.setHandler(new FrontendCommandHandler(source));
        if (LOGGER.isInfoEnabled()) {
            StringBuilder s = new StringBuilder();
            s.append(source).append('\'').append(auth.user).append("' login success");
            byte[] extra = auth.extra;
            if (extra != null && extra.length > 0) {
                s.append(",extra:").append(new String(extra));
            }
            LOGGER.info(s.toString());
        }
        ByteBuffer buffer = source.allocate();
        source.write(source.writeToBuffer(AUTH_OK, buffer));
    }

 

客户端收到此包后,就可以发送各种sql语句,进行数据库操作了。在上面我们也看到了

前端连接也绑定了新的Handler--FrontendCommandHandler, 用于处理各种sql语句类型。进行数据处理分发。

 

笔者第一次写blog,此次的blog也是自己阅读MyCat代码的一次笔记。 主要从代码角度介绍了MyCat如何模仿MysqlServer进行Auth认证,具体的Auth包协议格式,网上已经有不少博文有描述。

后续将继续就MyCat,在阅读代码的过程中,记下自己的理解。与大家互相探讨。

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

你可能感兴趣的:(java,mysql,中间件,Mycat)