[Laravel] Problemas com MySQL 8 e Eloquent

Neste post irei mostrar como proceder para rodar o MySQL 8 no Laravel. Algumas mudanças ocorreram nesta nova versão do MySQL e você provavelmente deve estar arrancando os cabelos para tentar resolver essa situação e por isso chegou até aqui. Mas calma! Vamos resolver isso? Só peço que você leia com atenção e não saia igual um desesperado copiando e colando, ok?

Informação aos tripulantes

Vou usar o Ubuntu, que é um Linux Debian-like, e não é o objetivo mostrar os procedimentos em outros tipos de Sistema Operacional, por este motivo, veja como adaptar os comandos que são usados no meu sistema para o seu.

Também estou partindo do pressuposto que você já tem o MySql 8 instalado, pois não faria sentido você estar fazendo esta leitura.

Todavia, o procedimento de instalação no site da Oracle não especifica que a biblioteca libmysqlclient20 seja instalada.

Ela é importante para o que vamos fazer, pois é ela quem disponbiliza o acesso ao plugin de autenticação que vamos utilizar.

Por isso, verifique se ela já está instalada antes de realizar os procedimentos a seguir:

sudo apt install libmysqlclient20

Qual o problema com o Mysql 8?

O MySQL 8 é a nova versão do tradicional banco de dados MySQL popularmente conhecido por rodar lado a lado com o PHP, que pulou da versão 5.7 para a 8. Até aí tudo bem, afinal o PHP pulou da versão 5.6 para a 7. Mas enfim… o problema é que modificaram o mecanismo de autenticação de usuários do MySQL adicionando um tal de plugin caching_sha2_password, que supostamente, conforme diz na documentação: caching_sh2_password: implementa autenticação SHA-256 (como sha25_password), mas usa cacheamento para melhor performance e possui características adicionais para aplicabilidade mais ampla – o que pode ser resumido em: vamos ferrar com a vida de todo mundo e criar uma versão do MySQL que por padrão vai ser incompatível com o que já rodava em versões anteriores… e foda-se (grifo do autor)!

Bom, mas como para toda merda sempre existe um papel higiênico, jornal, ou folha de bananeira… é possível informar ao MySQL 8 o plugin de autenticação e resolver uma parte do problema.

Se você instalar o MySQL 8 e tentar rodar algo como:

php artisan migrate

Certamente, você será informado de algo como:

SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client
...
PDOException::("PDO::__construct(): The server requested authentication method unknown to the client [caching_sha2_password]")

Perfeito?… não, né? Uma bosta mesmo! …

Isso quer dizer que o PHP não conseguiu se conectar com o MySQL porque o usuário informado ao PDO não possui o plugin de autenticação caching_sha2_password.

Apartir daí, ja podemos resolver a primeira parte do problema, mas antes, vamos fazer um teste para você entender o problema e não sair modificando sem saber o que está fazendo.

Conecte ao MySQL 8 e crie um usuário:

mysql -uroot -p
use mysql;
CREATE USER 'newbie'@'localhost' IDENTIFIED BY '123456';
SHOW CREATE USER 'newbie'@'localhost'\G

E você verá algo como:

CREATE USER for newbie@localhost: CREATE USER 'newbie'@'localhost' IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$n3~W>b.>h@6=Dx9;(mqdx2.6Di5OvTF8BDOKJuFqdHee9FAOBliwzvEQlly/' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT

Veja a parte do resultado onde consta caching_sha2_password.

Perceba também que a senha está num formato que usa uma criptografia.

O MySQL permite criar usuários especificando o plugin de autenticação, e conforme o resultado acima poderíamos fazer o seguinte:

CREATE USER 'newbie'@'localhost' IDENTIFIED WITH caching_sha2_password BY '123456';

Percebeu a adição do plugin de autenticação?

... WITH caching_sha2_password ...

Isso significa que podemos usar qualquer plugin de autenticação disponível e alterar o comportamento caching_sha2_password.

Modificando o plugin de autenticação do MySQL

Sabendo que o problema é com o plugin, então a solução no tocante ao MySQL é alterar o plugin, e para fazer isso é preciso mudar o arquivo de configuração do MySQL.

Sim, puta que pariu! tem que mexer no arquivo de configuração do MySQL, pois o problema é numa camada bem baixa, e não dá pra agir via configuração do PHP, pois o revoltado aqui é o MySQL mesmo! No momento, tem gente na comunidade PHP que já está dando seus pulinhos, e talvez na próxima versão do PHP 7.3, ou alguma versão 7.2.* os desenvolvedores do PHP criam uma solução, mas até lá… infelizmente, a solução é modificar o arquivo de configuração, o que acaba por limitar algumas hospedagens ou revendas que não oferecem acesso às configurações do MySql.

Como estou no Ubuntu, o arquivo de configuração do MySQL estará em:

vim /etc/mysql/mysql.conf.d/mysqld.cnf

Veja onde o mesmo se encontra no seu sistema!

Então, a primeira solução é informar ao MySQL que você quer usar outro plugin.

O plugin que precisamos é o mysql_native_password.

Esse plugin é um plugin padrão do MySql e não foi removido, por questões óbvias já conhecidas, e podemos utilizá-lo seguramente!

Para tal, abra o arquivo de configuração do MySql do seu Sistema Operacional.

Uma dica, o arquivo de configuração precisa conter a chave:

  [mysqld]

Que é o processo do MySql que carrega o plugin.

No final do bloco que contém essa chave, adicione:

[default_authentication_plugin=mysql_native_password]

Reinicie o MySql.

sudo systemctl restast mysql

Somente após reiniciado o MySql, voltamos ao MySql:

mysql -uroot -p
use mysql;
SHOW CREATE USER 'newbie'@'localhost'\G

E você verá o mesmo de antes! Problema? Siiiiiim!

Como os plugins usam autentiações diferentes, o Servidor do MySql vai retornar o mesmo erro do começo e o PDO do PHP também.

Então, uma solução rápida seria redefinir a senha do usuário, pois isso fará com que o MySql mude a senha para o novo padrão do plugin que foi utilizado.

Vale lembrar, que as permissões do usuário não são alteradas, uma vez que elas não usam o plugin de autenticação.

Faça o teste:

ALTER USER 'newbie'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';

Preste atenção que PASSAMOS o driver para atualizar a senha de um usuário já existente!

E verifique, que a saída agora é diferente:

CREATE USER for newbie@localhost: CREATE USER 'newbie'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT

Veja que agora o plugin usado é o mysql_native_password.

Ótimo! Entendeu o problema? Maravilha?

Dai pra frente, todos os novos usuários podem ser criados sem passar o WITH mysql_native_password, pois assim como acontecia antes com o caching_sha2_password o MySql vai usar esse plugin por padrão.

Permissões do Usuário

No caso, só antes de prosseguir com o Laravel, talvez você não tenha dado permissões ao usuário, então execute:

GRANT ALL PRIVILEGES ON *.* TO 'newbie'@'localhost' WITH GRANT OPTION
FLUSH PRIVILEGES;

Mas lembre-se que estas permissões são para o ambiente de desenvolvimento, dependendo do que for fazer em produção reduza as privilégios.

Configurando o Laravel

Com esse procedimento os usuários criados já podem rodar no PHP normalmente, no entanto, o Laravel, para compatilizar como MySql 5.7, utilizar o modo strict, o que pode ser encontrado no arquivo config/database.php, conforme:

...
'mysql' => [
    'driver' => 'mysql',
    'host' => env('DB_HOST', '127.0.0.1'),
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'unix_socket' => env('DB_SOCKET', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'strict' => true,
    'engine' => null,
],
...

O que a diretiva 'strict' => true faz é definir variável global no MySql:

...
set session sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
...

Caso tenha curiosidade, esse trecho de código está no final do arquivo vendor/laravel/framework/src/Illuminate/Database/Connectors/MySqlConnector.php:

...
    /**
     * Get the query to enable strict mode.
     *
     * @return string
     */
    protected function strictMode()
    {
        return "set session sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'";
    }
...

Bom, se voce rodar o comando php artisan migrate sem mudar esse valor do arquivo de configuração, você verá o erro:

...
SQLSTATE[42000]: Syntax error or access violation: 1231 Variable 'sql_mode' can't be set to the value of 'NO_AUTO_CREATE_USER'
...

Usar o modo strict não causará falha de seguranças, somente mudará o comportamento do MySql para algo similar ao que era na versão 5.6, e que não elimina nenhuma das novas funcionalidades do MySql 8.*, então mude:

...
'mysql' => [
...
    'strict' => true,
...
],...

Para:

...
'mysql' => [
...
    'strict' => false,
...
],...

Para:

E pronto! Tudo deverá estar funcionando normalmente, e agora você é uma pessoa fodona que já pode usar o MySql 8 e suas novas funcionalidades.

A saber… eu não testei as novas funcionalidades do MySql com o Eloquent… então, talvez esse assunto gere um novo post.

É isso aí! Espero ter lhe ajudado!

Abraço do Giba!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.