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,在阅读代码的过程中,记下自己的理解。与大家互相探讨。
版权声明:本文为博主原创文章,未经博主允许不得转载。