55 mysql 的登录认证流程

发布于:2024-09-19 ⋅ 阅读:(115) ⋅ 点赞:(0)

前言

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

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

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

 

 

认证失败的大体流程

大概的流程是这样 

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

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

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

b7f2f81ceff12b4e7dd93170e3ee1aa0.png

 

服务器发送 salt 的信息如下 

3449c1caa4f7b03c71459f6a5a1c378f.png 

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

d78e4f8c3d6f517995695d770fa33880.png

 

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

3c87143a1162269942ef12ed6cb48496.png

 

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

077e1db2fab82ef6d406988a09fcc33e.png 

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

c33f23fa6a16a0c2e15e577a3558f018.png 

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

23fefefbe4a62457a9849bcbbb3bff45.png

  

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

b5be01b8a38cc7e162af1fa3c3ce50f2.png

 

 

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

这里同上, 几种情况 

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

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

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

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

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

2ebb889daa7b6b93749ba8d72d96369f.png

 

 

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

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

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

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

d078fa1cad2859b912351ce7f33560e7.png

 

 

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

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

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

可以参考学习一下, 需要导入 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";
        }
    }

}

 

运行处理, 信息如下 

8decf59dbd830698990ae6a8e1658f78.png

 

 

完 

 

 

 


网站公告

今日签到

点亮在社区的每一天
去签到