0
点赞
收藏
分享

微信扫一扫

55 mysql 的登录认证流程


前言

这里我们来看一下 mysql 的认证的流程 

我们这里仅仅看 我们最常见的一个 认证的处理流程

我们经常会登录的时候 碰到各种异常信息

 

 

认证失败的大体流程

大概的流程是这样 

客户端和服务器建立连接之后, 服务器向客户端发送 salt

然后 客户端根据 salt 将客户端传入的密码加密之后, 以及相关登录信息传递给服务器 

然后 服务器进行验证, 验证失败, 响应对应的错误信息给客户端 

55 mysql 的登录认证流程_服务器

 

服务器发送 salt 的信息如下 

55 mysql 的登录认证流程_服务器_02

 

客户端发送的认证请求如下 

55 mysql 的登录认证流程_客户端_03

 

 服务器响应的认证失败信息如下 

55 mysql 的登录认证流程_connection_04

 

服务器发送 slat 的信息是在这里, 这里的 salt 是随机生成的 

55 mysql 的登录认证流程_connection_05

 

读取客户端的加密之后的信息 

55 mysql 的登录认证流程_login_06

 

然后是 密码的验证, 我们这里的流程是这样, 这里 数据库用 root 用户没有密码, 但是客户端这边传入了密码, 因此这里直接响应了 CA_AUTH_USER_REDENTIALS

55 mysql 的登录认证流程_服务器_07

  

然后接着是 响应错误信息给客户端, 这里是响应 1045 ACCESS_DENIED_ERROR “Access denied for user 'root'@'192.168.220.1' (using password: YES)”

55 mysql 的登录认证流程_客户端_08

 

 

Access denied for user 'root'@'192.168.220.1' (using password: YES) 

这里同上, 几种情况 

首先需要传递密码, 其次是 root用户密码为空 或者 密码不对 会响应如下问题

上面演示了 mysql 密码为空, 但是 我输入了密码 之后的校验情况

我们现在来看一下 密码对不上的情况

用到了一些加密的特性来验证, 客户端输入的密文和数据库是否匹配 

scramble_arg 表示的是客户端传入的密文, message 表示数据库的密文, hash_stage2 表示此次登录验证的 slat 

55 mysql 的登录认证流程_mysql_09

 

 

Access denied for user 'root'@'192.168.220.1' (using password: NO) 

这种情况就是 该用户有密码, 但是客户端 未传递 密码

验证是在这里, 如果数据库中该用户也没有密码, 直接 OK, 否则 响应 CR_AUTH_USER_CREDENTIALS

到后面的错误输出环节, 得到的错误信息 就是上面

55 mysql 的登录认证流程_login_10

 

 

客户端加密, 服务器验证的逻辑仿写 

这里主要是 实现了一个 客户端这边 传递的密码的加密方式的处理

以及 服务器这边 验证 客户端秘钥, 服务器秘钥 的一个验证方式

可以参考学习一下, 需要导入 mysql-driver, netty 等等相关工具 

salt 是服务器的 ServerGreeting 传递回来的, 是一个 20自己的随机字节序列

clientPwd 是根据用户名, 密码 salt 加密之后的一个结果 

serverPwd 是 mysql 中 mysql.user 库中该用户记录的 authentication_string 秘钥序列

/**
 * Test09MysqlLoginEncrypt
 *
 * @author Jerry.X.He
 * @version 1.0
 * @date 2023/8/9 9:31
 */
public class Test09MysqlLoginEncrypt {

    // Test09MysqlLoginEncrypt
    public static void main(String[] args) throws Exception {

        String salt = ("2c 4f 04 2a 30 13 69 71").replaceAll("\\s+", "")
                + ("03 17 0a 1d 64 55 7e 68 1f 19 73 0a").replaceAll("\\s+", "");
        String clientPwd = "012cb36acb2a4c77217d8d70dc43e058c1c6448a";
        String serverPwd = "81F5E21E35407D884A6CD4A731AEBFB6AF209E1B";

        String clientPwdEncoded = ByteBufUtil.hexDump(clientEncryptPwd("root", "root", salt));
        boolean clientPwdIfMatch = checkClientPwdIfMatch(clientPwd, salt, serverPwd);
        AssertUtils.assert0(clientPwd, clientPwdEncoded, " check if clientEncryptPwd match ");
        AssertUtils.assert0(clientPwdIfMatch, " checkClientPwdIfMatch match ");
        int x = 0;

    }

    /**
     * checkClientPwdIfMatch
     *
     * @return boolean
     * @author Jerry.X.He
     * @date 2023/8/9 10:31
     */
    public static boolean checkClientPwdIfMatch(String clientPwd, String salt, String serverPwd) {
        byte[] saltBytes = ByteBufUtil.decodeHexDump(salt);
        byte[] pwdInClient = ByteBufUtil.decodeHexDump(clientPwd);
        byte[] pwdInDb = ByteBufUtil.decodeHexDump(serverPwd);

        MessageDigest digest = DigestUtils.getSha1Digest();
        digest.reset();
        digest.update(saltBytes);
        digest.update(pwdInDb);
        byte[] pwdSaltUpdated = digest.digest();

        my_crypt(pwdSaltUpdated, pwdSaltUpdated, pwdInClient);

        digest.reset();
        digest.update(pwdSaltUpdated);
        byte[] pwdFinalUpdated = digest.digest();

        boolean clientPwdEqualsDb = ByteBufUtil.hexDump(pwdFinalUpdated).equalsIgnoreCase(serverPwd);
        return clientPwdEqualsDb;
    }

    /**
     * clientEncryptPwd
     *
     * @return byte[]
     * @author Jerry.X.He
     * @date 2023/8/9 10:22
     */
    public static byte[] clientEncryptPwd(String username, String password, String salt) {
        MysqlNativePasswordPlugin plugin = new MysqlNativePasswordPlugin();
        NativeProtocol protocol = new MyNativeProtocol(new NullLogger(""));
        plugin.init(protocol);
        NativePacketPayload fromServer = new NativePacketPayload(ByteBufUtil.decodeHexDump(salt));
        List<NativePacketPayload> toSaveList = new ArrayList<>();
        plugin.setAuthenticationParameters(username, password);
        plugin.nextAuthenticationStep(fromServer, toSaveList);
        return toSaveList.get(0).getByteBuffer();
    }

    /**
     * my_crypt
     *
     * @return void
     * @author Jerry.X.He
     * @date 2023/8/9 10:21
     */
    public static void my_crypt(byte[] to, byte[] s1, byte[] s2) {
        for (int i = 0; i < s1.length; i++) {
            to[i] = (byte) (s1[i] ^ s2[i]);
        }
    }

    /**
     * Test09MysqlLoginEncrypt
     *
     * @author Jerry.X.He
     * @version 1.0
     * @date 2023/8/9 10:19
     */
    private static class MyNativeProtocol extends NativeProtocol {
        public MyNativeProtocol(Log logger) {
            super(logger);
        }

        @Override
        public String getPasswordCharacterEncoding() {
            return "UTF-8";
        }
    }

}

 

运行处理, 信息如下 

55 mysql 的登录认证流程_服务器_11

 

 

完 

 

 

 

举报

相关推荐

0 条评论