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,在阅读代码的过程中,记下自己的理解。与大家互相探讨。
版权声明:本文为博主原创文章,未经博主允许不得转载。
本文详细解读了MyCat处理认证请求的过程,包括生成认证数据、发送握手包、接收并验证客户端提供的密码等关键步骤,以及如何在接收到认证信息后检查用户名、数据库等连接参数,最终通过发送成功响应包来完成认证流程。
234

被折叠的 条评论
为什么被折叠?



