源码研究mycat之mysql通信协议篇之握手认证协议,2024年最新VB基于设计与实现

public void setConnectionId(int connectionId) {

this.connectionId = connectionId;

}

public byte[] getAuthPluginDataPart1() {

return authPluginDataPart1;

}

public void setAuthPluginDataPart1(byte[] authPluginDataPart1) {

this.authPluginDataPart1 = authPluginDataPart1;

}

public int getServerCapability() {

return serverCapability;

}

public void setServerCapability(int serverCapability) {

this.serverCapability = serverCapability;

}

public byte getCharacterSet() {

return characterSet;

}

public void setCharacterSet(byte characterSet) {

this.characterSet = characterSet;

}

public int getServerStatus() {

return serverStatus;

}

public void setServerStatus(int serverStatus) {

this.serverStatus = serverStatus;

}

public byte[] getAuthPluginDataPart2() {

return authPluginDataPart2;

}

public void setAuthPluginDataPart2(byte[] authPluginDataPart2) {

this.authPluginDataPart2 = authPluginDataPart2;

}

public byte[] getAuthPluginName() {

return authPluginName;

}

public void setAuthPluginName(byte[] authPluginName) {

this.authPluginName = authPluginName;

}

}

4.2 认证包


package persistent.prestige.console.mysql.protocol;

import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.channels.SelectableChannel;

import java.nio.channels.SocketChannel;

import persistent.prestige.console.mysql.MysqlClient;

import persistent.prestige.console.mysql.utils.SecurityUtil;

import persistent.prestige.console.mysql.utils.SeqUtils;

/**

  • 验证数据包

  • 客户端在收到服务端的握手协议后,需要向服务器验证权限(用户名进行登录)

  • @author dingwei2

  • Protocol::HandshakeResponse41:

  • 4 capability flags, CLIENT_PROTOCOL_41 always set

4 max-packet size

1 character set

string[23] reserved (all [0])

string[NUL] username

if capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA {

lenenc-int length of auth-response

string[n] auth-response

} else if capabilities & CLIENT_SECURE_CONNECTION {

1 length of auth-response

string[n] auth-response

} else {

string[NUL] auth-response

}

if capabilities & CLIENT_CONNECT_WITH_DB {

string[NUL] database

}

if capabilities & CLIENT_PLUGIN_AUTH {

string[NUL] auth plugin name

}

if capabilities & CLIENT_CONNECT_ATTRS {

lenenc-int length of all key-values

lenenc-str key

lenenc-str value

if-more data in ‘length of all key-values’, more keys and value pairs

}

*/

public class Auth41Packet extends Packet {

private Handshake10Packet handshake10Packet;

private int capabilityFlags;

private int maxPacketSize = 1 << 31 - 1;

private byte characterSet;

private final static byte[] reserved = new byte[23];

private byte[] username;

private byte authResponseLen;

private byte[] authResponse;

private byte[] database = MysqlClient.DB.getBytes();

private byte[] authPluginName;

static {

for (int i = 0; i < 23; i++) {

reserved[i] = FILLER;

}

}

private Auth41Packet(Handshake10Packet handshake10Packet) {

this.handshake10Packet = handshake10Packet;

}

public static final Auth41Packet newInstance(Handshake10Packet handshake10Packet) {

Auth41Packet packet = new Auth41Packet(handshake10Packet);

packet.setCapabilityFlags(getCapabilities());

// packet.setCapabilityFlags(handshake10Packet.getServerCapability());

packet.setCharacterSet(handshake10Packet.getCharacterSet());

packet.setUsername(MysqlClient.USERNAME.getBytes());

try {

if (handshake10Packet.getAuthPluginDataPart2() == null) {

packet.setAuthResponse(SecurityUtil.scramble411_2(MysqlClient.PWD.getBytes(“UTF-8”),

handshake10Packet.getAuthPluginDataPart1()));

} else {

final byte[] auth1 = handshake10Packet.getAuthPluginDataPart1();

final byte[] auth2 = handshake10Packet.getAuthPluginDataPart2();

byte[] seed = new byte[auth1.length + auth2.length - 1];

System.arraycopy(auth1, 0, seed, 0, auth1.length);

System.arraycopy(auth2, 0, seed, auth1.length, auth2.length - 1);

// 关于seed 为什么只取auth2的 长度-1,是因为

// Due to Bug#59453 the auth-plugin-name is missing the

// terminating NUL-char

// in versions prior to 5.5.10 and 5.6.2.;

// 由于本示例代码的目的是为了学习mysql通信协议,所以这里就不做版本方面的兼容了。直接取auth2

// 0-length-1个字节参与密码的加密

byte[] authResponse = SecurityUtil.scramble411_2(MysqlClient.PWD.getBytes(), seed);

packet.setAuthResponse(authResponse);

packet.setAuthResponseLen((byte) authResponse.length);

}

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

packet.setAuthPluginName(handshake10Packet.getAuthPluginName());

return packet;

}

public int getPacketLength() {

// 32 +

int len = 32;

if (username != null) {

len += username.length + 1;// 1字节填充

}

if (authResponseLen > 0) {

len += 1 + authResponseLen; // 1字节填充

}

if (database != null) {

len += database.length + 1; // 1字节填充

}

if (authPluginName != null) {

len += authPluginName.length + 1; // 1字节填充

}

return len;

}

/**

  • @param channel

  • @return

*/

public int write(SelectableChannel channel) throws IOException {

int packetLen = this.getPacketLength() + HEAD_LENGTH;

byte seq = SeqUtils.getSeq(channel);

ByteBuffer buf = ByteBuffer.allocate(packetLen);

MysqlMessage msg = new MysqlMessage(buf);

msg.putUB3(packetLen - HEAD_LENGTH);

msg.put(seq); // 包头 3字节长度 + 1 字节序列号

msg.putInt(this.getCapabilityFlags());

msg.putInt(this.maxPacketSize);

msg.put(this.getCharacterSet());

msg.putBytes(reserved);

msg.putBytes(this.username);

msg.put(FILLER);

msg.putBytes(this.authResponse);

msg.put(FILLER);

msg.putBytes(this.database);

msg.put(FILLER);

msg.putBytes(authPluginName);

msg.put(FILLER);

msg.flip();

SocketChannel c = (SocketChannel) channel;

return c.write(msg.nioBuffer());

}

public static final int getCapabilities() {

return CLIENT_LONG_PASSWORD | CLIENT_FOUND_ROWS | CLIENT_CONNECT_WITH_DB |

// CLIENT_COMPRESS ,压缩协议,为了简单,暂不开启

CLIENT_LOCAL_FILES | CLIENT_IGNORE_SPACE | CLIENT_PROTOCOL_41 | CLIENT_INTERACTIVE

| CLIENT_IGNORE_SIGPIPE | CLIENT_TRANSACTIONS |

// CLIENT_SECURE_CONNECTION |

// CLIENT_MULTI_STATEMENTS |

CLIENT_MULTI_RESULTS | CLIENT_PS_MULTI_RESULTS | CLIENT_PLUGIN_AUTH;

// CLIENT_CONNECT_ATTRS |

// CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS |

// CLIENT_SESSION_TRACK |

// CLIENT_DEPRECATE_EOF;

}

public Handshake10Packet getHandshake10Packet() {

return handshake10Packet;

}

public void setHandshake10Packet(Handshake10Packet handshake10Packet) {

this.handshake10Packet = handshake10Packet;

}

public int getCapabilityFlags() {

return capabilityFlags;

}

public void setCapabilityFlags(int capabilityFlags) {

this.capabilityFlags = capabilityFlags;

}

public int getMaxPacketSize() {

return maxPacketSize;

}

public void setMaxPacketSize(int maxPacketSize) {

this.maxPacketSize = maxPacketSize;

}

public byte getCharacterSet() {

return characterSet;

}

public void setCharacterSet(byte characterSet) {

this.characterSet = characterSet;

}

public byte[] getUsername() {

return username;

}

public void setUsername(byte[] username) {

this.username = username;

}

public static byte[] getReserved() {

return reserved;

}

public byte getAuthResponseLen() {

return authResponseLen;

}

public void setAuthResponseLen(byte authResponseLen) {

this.authResponseLen = authResponseLen;

}

public byte[] getAuthResponse() {

return authResponse;

}

public void setAuthResponse(byte[] authResponse) {

this.authResponse = authResponse;

}

public byte[] getDatabase() {

return database;

}

public void setDatabase(byte[] database) {

this.database = database;

}

public byte[] getAuthPluginName() {

return authPluginName;

}

public void setAuthPluginName(byte[] authPluginName) {

this.authPluginName = authPluginName;

}

}

4.3 mysql client


package persistent.prestige.console.mysql;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.SocketChannel;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Set;

import persistent.prestige.console.mysql.connection.Connection;

import persistent.prestige.console.mysql.connection.ConnectionFactory;

import persistent.prestige.console.mysql.protocol.Auth41Packet;

import persistent.prestige.console.mysql.protocol.ErrorPacket;

import persistent.prestige.console.mysql.protocol.Handshake10Packet;

import persistent.prestige.console.mysql.protocol.MysqlMessage;

import persistent.prestige.console.mysql.protocol.OkPacket;

public class MysqlClient {

public static final String MYSQL_HOST = “10.2.35.23”;

public static final int MYSQL_PORT = 3306;

public static final String DB = “demo”;

public static final String USERNAME = “peter”;

public static final String PWD = “peter”;

private Map<SocketChannel, Connection> connMap = new HashMap<SocketChannel, Connection>();

public static void main(String[] args) {

MysqlClient client = new MysqlClient();

(new Thread(client.new Client())).start();

}

private class Client implements Runnable {

@Override

public void run() {

// TODO Auto-generated method stub

Selector selector = null;

SocketChannel scc = null;

try {

selector = Selector.open();

scc = SocketChannel.open();

scc.configureBlocking(false);

scc.register(selector, SelectionKey.OP_CONNECT);

scc.connect(new InetSocketAddress(MYSQL_HOST, MYSQL_PORT));

Set selOps = null;

loop:

while(true) {

int n = selector.select();

selOps = selector.selectedKeys();

if(selOps == null || selOps.isEmpty()) {

continue;

}

try {

for(Iterator it = selOps.iterator(); it.hasNext(); ) {

SelectionKey key = it.next();

if(!key.isValid()) {

key.cancel();

}

if(key.isReadable()) { //可读

System.out.println(“读事件触发”);

SocketChannel sc = (SocketChannel)key.channel();

ByteBuffer recvBuffer = ByteBuffer.allocate(1024); // 固定1024字节用来接收数据

int r = 0;

int remaining = recvBuffer.remaining();

int localRead = 0;

while( (r = sc.read(recvBuffer) ) > 0 ) { //一次性读完通道数据

remaining -= r;

localRead += r;

if(r > 0 && remaining == 0 ) { //接收缓存区不足,扩容一倍

ByteBuffer tempBuf = ByteBuffer.allocate( recvBuffer.capacity() << 1 );

recvBuffer.flip();//变成读模式

tempBuf.put(recvBuffer);

remaining = recvBuffer.remaining();

recvBuffer = tempBuf;

}

}

System.out.println(“可读数据:” + localRead);

if(r == -1 && localRead < 1) { //链路关闭了

System.out.println(“收到字节为-1,服务端关闭连接”);

break loop;

}

Connection conn = getConn(sc);

if(!conn.isHandshake()) { //未验证,发送握手协议包

//开始解析服务端发送过来的握手协议

recvBuffer.flip();//变成可读模式

Handshake10Packet handshkakePacket = Handshake10Packet.newInstance( new MysqlMessage(recvBuffer));

System.out.println(handshkakePacket);

if(handshkakePacket != null && !recvBuffer.hasRemaining()) { //如果解析出完整的包,并且recvBuffer

//取消读事件

clearOpRead(key); //这里不考虑只接收到一半的数据包,继续下一次包解析,本示例主要关注的点mysql通信协议

}

//注册写事件

key.attach(handshkakePacket);

} else if (!conn.isAuth()) { // 未成功授权,尝试解析服务端包

//开始解析服务器授权响应报文

recvBuffer.flip();//变成可读模式

MysqlMessage msg = new MysqlMessage(recvBuffer);

int packetLen = msg.getPacketLength();

byte packetSeq = msg.getPacketSeq();

short pType = msg.getPTypeByFrom1Byte();

System.out.println(“数据包类型:” + pType);

if(pType == 0) { //OK数据包 //此处不考虑其他情况

OkPacket ok = OkPacket.newInstance(msg, packetSeq, packetLen);

System.err.println(ok);

conn.setAuth(true);

System.out.println(“成功通过验证”);

//接下来,取消读事件,开始发送命令给服务端,-----测试mysql的请求命令。

clearOpRead(key);

//目前暂时退出客户端

break loop;

} else if(pType == 0xFF) { // error 包

System.out.println(“错误包”);

ErrorPacket errorPacket = ErrorPacket.newInstance(msg, packetSeq, packetLen);

System.out.println(errorPacket);

//然后退出 客户端

break loop;

} else {

System.out.println(“收到暂不支持的包,将退出”);

break loop;

}

} else { //其他响应包

}

addOpWrite(key);

} else if(key.isWritable()) { // 可写

System.out.println(“写事件触发”);

SocketChannel sc = (SocketChannel)key.channel();

Connection conn = getConn(sc);

Object attachment = key.attachment();

if( attachment instanceof Handshake10Packet ) {

Handshake10Packet handshkakePacket = (Handshake10Packet) attachment;

Auth41Packet handshakeResponse = Auth41Packet.newInstance(handshkakePacket);

int wc = handshakeResponse.write(key.channel());

System.out.println(“写入通道数据:” + wc + “字节”);

clearOpWrite(key);

conn.setHandshake(true);

}

addOpRead(key);

} else if(key.isConnectable()) {

if(scc.isConnectionPending()) {

scc.finishConnect();

System.out.println(“完成tcp连接”);

}

scc.register(selector, SelectionKey.OP_READ);

}

it.remove();

}

} catch (Throwable e) {

e.printStackTrace();

}

}

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数同学面临毕业设计项目选题时,很多人都会感到无从下手,尤其是对于计算机专业的学生来说,选择一个合适的题目尤为重要。因为毕业设计不仅是我们在大学四年学习的一个总结,更是展示自己能力的重要机会。

因此收集整理了一份《2024年计算机毕业设计项目大全》,初衷也很简单,就是希望能够帮助提高效率,同时减轻大家的负担。
img
img
img

既有Java、Web、PHP、也有C、小程序、Python等项目供你选择,真正体系化!

由于项目比较多,这里只是将部分目录截图出来,每个节点里面都包含素材文档、项目源码、讲解视频

如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
img

  • wc + “字节”);

clearOpWrite(key);

conn.setHandshake(true);

}

addOpRead(key);

} else if(key.isConnectable()) {

if(scc.isConnectionPending()) {

scc.finishConnect();

System.out.println(“完成tcp连接”);

}

scc.register(selector, SelectionKey.OP_READ);

}

it.remove();

}

} catch (Throwable e) {

e.printStackTrace();

}

}

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数同学面临毕业设计项目选题时,很多人都会感到无从下手,尤其是对于计算机专业的学生来说,选择一个合适的题目尤为重要。因为毕业设计不仅是我们在大学四年学习的一个总结,更是展示自己能力的重要机会。

因此收集整理了一份《2024年计算机毕业设计项目大全》,初衷也很简单,就是希望能够帮助提高效率,同时减轻大家的负担。
[外链图片转存中…(img-9RhyWgWg-1712585457159)]
[外链图片转存中…(img-HwbczjaV-1712585457160)]
[外链图片转存中…(img-X2Jouzab-1712585457160)]

既有Java、Web、PHP、也有C、小程序、Python等项目供你选择,真正体系化!

由于项目比较多,这里只是将部分目录截图出来,每个节点里面都包含素材文档、项目源码、讲解视频

如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
[外链图片转存中…(img-nzJhgjt3-1712585457160)]

  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值