MySQLサーバにアクセスできるのに、insertなどができない時の対処

投稿者: | 2020年11月13日

PHP環境ができたので、Sign up/Sign in/Logout機能を作ってみることにしました。しかし、PHP+MySQLを触るのが初めてだったので、周りを巻き込んで大騒ぎしてたのですが、フォロワーさんのお陰で解決したので、解決策とやったことをまとめて行きます。また、MySQLに関しては全くの初心者なので、そのあたりもまとめています。

結論から言えば、接続しているユーザの権限が適切に与えられていませんでした。

1.問題

phpで作る簡素なログイン機能を参考にログインフォームを作成していました。しかし、どうしても例外処理に飛んでしまい、うまくいきませんでした。正確には、MySQLサーバにアクセスはできるが、テーブルにinsertなど操作ができない状態でした。

環境は、こちらです。(Docker上にPHP+MySQL+Nginx)

以下、phpで作る簡素なログイン機能に出てくるファイル名などを用います。ただし、この記事だとsignUp.phpが2つあるので、ログインフォームのある方をindex.phpととました。

2.config.phpを変える

Dockerを用いた場合、MySQLのhost名はdocker-compose.ymlで書いたサービス名にします。その他はそのままで大丈夫です。

<?php
ini_set('display_errors', 1);

define('DSN', 'mysql:host=db;dbname=user;charset=utf8');
define('DB_USER', 'test_user');
define('DB_PASS', 'password');

3.ロギングを有効にする

クエリが正常に来ているか確認するためにロギングを有効にします。

まずは、dockerコンテナに入りますが、先に実行中のコンテナを確認します。

docker ps

私の環境だと次のように表示されます。

CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS              PORTS                                NAMES
6eb12cb50593        nginx:latest                   "/docker-entrypoint.…"   11 hours ago        Up 47 minutes       0.0.0.0:8080->80/tcp                 php_nginx_1
877451a490f0        php_php                        "docker-php-entrypoi…"   11 hours ago        Up 47 minutes       9000/tcp                             php_php_1
b2d3f682dc86        phpmyadmin/phpmyadmin:latest   "/docker-entrypoint.…"   11 hours ago        Up 47 minutes       0.0.0.0:8888->80/tcp                 php_phpmyadmin_1
73b29e0dbfb5        mysql:5.7                      "docker-entrypoint.s…"   11 hours ago        Up 47 minutes       33060/tcp, 0.0.0.0:13306->3306/tcp   php_db_1

MySQLコンテナに入りたいので、次のようにします。

docker exec -it php_db_1 /bin/bash

そして、MySQLサーバに接続します。

mysql -u root -p

これでroot権限で接続できます。パスワードが求められると思いますが、設定したパスワードで接続してください。

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 25
Server version: 5.7.32 MySQL Community Server (GPL)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

こういう表示が出れば接続成功です。

さて、現在の設定を確認します。

show variables like 'general_log%';
+------------------+---------------------------------+
| Variable_name    | Value                           |
+------------------+---------------------------------+
| general_log      | OFF                             |
| general_log_file | /var/lib/mysql/73b29e0dbfb5.log |
+------------------+---------------------------------+
2 rows in set (0.01 sec)

OFFになっているので、これをONにします。

set global general_log = on;

すると即座にログファイルが作成されます。

4.トラブルシューティング

4-1.MySQLにユーザを作成する

config.phpに記述したユーザをMySQL側にも作成します。

MySQLサーバにアクセスして、次のコマンドを実行します。

create user 'test_user'@'%' identified by 'password';

Dockerの場合はlocalhostにアクセスできないので、ユーザのホスト名はDockerが割り当てたIPアドレスにするか、ワイルドカードの%にすると全てのホストと言う意味になり、接続できるようになります。

参考:Dockerで動かしているMySQLに作成したユーザーでPHPから接続できないときの対処方法

その後、ユーザに権限を与えますが、先に現在のユーザの権限を確認しておきます。

show grants for 'test_user'@'%';
+------------------------------------------------+
| Grants for test_user@%                         |
+------------------------------------------------+
| GRANT ALL PRIVILEGES ON *.* TO 'test_user'@'%' |
+------------------------------------------------+
1 row in set (0.00 sec)

ALL PRIVILEGESならば、全ての権限が与えられています。例えば、ここが次のようになっていた場合、テーブルにinsertできません。

 +-------------------------------------------------+
| Grants for test_user@%                           |
+-------------------------------------------------+
| GRANT CREATE ON *.* TO 'test_user'@'%' |
+-------------------------------------------------+
1 row in set (0.00 sec)

この場合は、テーブルをCREATEできる権限しか与えられていません。その時は、そのユーザに権限を付与します。

grant all on *.* to 'test_user'@'%';

これで権限が付与されたはずなので、showコマンドで再び確認しましょう。ALLは全ての権限を与えるので、必要に合わせて権限を変えてください。

4-2.MySQLサーバにアクセスできるか確認

次のコードをindex.phpに書き込みます。コードはPHPのPDOでサクッとMySQLへの接続を確認するコードを参考にしています。

<?php

require_once('config.php');

try {
    /// DB接続を試みる
    $db  = new PDO(DSN, DB_USER, DB_PASS);
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $msg = "MySQL への接続確認が取れました。";
} catch (PDOException $e) {
    $isConnect = false;
    $msg       = "MySQL への接続に失敗しました。<br>(" . $e->getMessage() . ")";
}

そして、localhost:8080にアクセスして接続確認をします。接続できない場合は、エラーメッセージに沿ってトラブルシューティングを行って下さい。

4-3.SQLSTATE[HY000] [1045] Access denied for user

アクセスが拒否されています。MySQLにユーザを作成していないのが原因ですので、4-1に従ってユーザを作成、権限を付与してください。

参考:[「SQLSTATEHY000] [1045] Access denied for user ‘test’@’localhost’ (using password: YES)」のエラーが出る

4-4.ログファイルの確認

一度問題のあるものを動かして、ログファイルを確認してみましょう。

2020-11-12T18:10:59.319233Z     7 Connect   test_user@172.18.0.4 on user using TCP/IP
2020-11-12T18:10:59.319491Z     7 Query create table if not exists userDeta(
      id int not null auto_increment primary key,
      email varchar(255),
      password varchar(255),
      created timestamp not null default current_timestamp
    )
2020-11-12T18:10:59.362986Z     7 Query insert into userDeta(email,password) values ('test@gmail.com','$2y$10$Ox3rIN6P/9XeOQK5dxaKxusMpQdZdGOLsmDCbuaYYqfRB69hBzUx.')
2020-11-12T18:10:59.363620Z     7 Quit  

このように書かれています。そして、クエリを確認してみましょう。ここが正しいSQL文になっているかどうか調べてみましょう。

MySQL構文チェッカーなどで構文をチェックしてみます。または、同じSQL文をMySQLサーバ内で直接実行してみます。

問題があればそれを修正します。問題がないようならば、ユーザの権限などを見直してみてください。

5.おわりに

私はユーザに権限を付与するというのを忘れて、フォロワーさんを巻き込んで大騒ぎしていました。ユーザの権限という概念が頭になかったので勉強になりました。

とりあえずフォロワーさんの協力のおかげでログインフォームが正常に動くことがわかったので、本格的にツール作成に取り組んでいきたいと思います。

あと、Laravel入れたほうがいいよと言われたので、別イメージを作成してそっちで作業しようかなと思います。最終的にwebで公開するものなので、フレームワークを使用したほうがいいらしい。

作業には関係ないですが、MySQLサーバから切断するときは、

\q;

で、コンテナから抜ける場合は、

exit

です。

6.参考文献

MySQLに投げられたすべてのSQLクエリをロギングする

PHPのPDOでサクッとMySQLへの接続を確認するコード

「SQLSTATE[HY000\] [1045] Access denied for user ‘test’@’localhost’ (using password: YES)」のエラーが出る

Dockerで動かしているMySQLに作成したユーザーでPHPから接続できないときの対処方法

[MySQL]権限の確認と付与

SQL文で’INSERT INTO Pages VALUES(?, ?, ?)’のように、?を使う理由【198日目】

SQLインジェクション

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください