IPアドレスから住所を逆引きする!! in Laravel



# はじめに
<!-- 発端や概要を記載 -->
携わっていた案件で、セキュリティ向上のためログインした時のIPアドレスをもとに、
住所を割り出し、メール通知をしたいという要件がありました。

Google 検索した時の一番下に出るような、どこの住所からアクセスしているかどうかみたいなやつですね。

それをLaravelで実装してみました。

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# パッケージの選定

1: https://github.com/Torann/laravel-geoip

https://github.com/Torann/laravel-geoip


2: https://github.com/stevebauman/location

https://github.com/stevebauman/location

今回は 2 を使用して実装を行なっていきます。

選定理由としては、

- issues が放置されていないか
- 直近でリリースが存在しているか(更新頻度)


の観点から選定をいたしました。

1 の https://github.com/Torann/laravel-geoip  の中身を見てみると、
(2022/01/10) issues が45件もありました。リリースだと直近だと21日前とはなっているものの、更新頻度はかなり少ないみたいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572374/272d3b9e-3102-21d8-b2c1-08f765707753.png)

2 つ目のパッケージを見てみます。
https://github.com/stevebauman/location を見てみると issues は2となっていますね。直近だと更新は5ヶ月前みたいです。

1つ目のパッケージと比べるとissuesも少なく、更新頻度も少なくはなかったです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572374/2c6f05d2-876b-ed78-5955-b7e88d9e5d05.png)

上記より、パッケージは 2つ目を使って実装することにいたしました。

issues があまりにも多いとパッケージ自体のメンテナンスをしている余裕がないプロジェクトだと思いますので、
こうなるとパッケージアップデートも難しくなり、バグ修正もされなくなるので利用者としてはあんまり使用しない方が良いと思っています。

要求している機能を満たしている場合でも、issues をしっかりと確認し、
どの程度選定しようとしているパッケージが稼働しているのかを把握することは重要ですので。

メンテナンスをされてるパッケージであれば、要望さえ出せば対応してくれたりするのでそのような観点からパッケージ選定をしました。


<a id="#Chapter2"></a>
# パッケージインストール

まずは、パッケージをインストールしていきます。

```php
composer require stevebauman/location
```

今回はLaravel 6 を使用していましたので、 config/app.php 配下には記載はしていないです。
インストールが終わりましたら config ディレクトリ配下に、location.php を用意します。
実装には必須ですので必ず行います。

```php
php artisan vendor:publish –provider=”Stevebauman\Location\LocationServiceProvider”
```


<a id="#Chapter3"></a>
# パッケージを使用して実装

パッケージのUsageを見ると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572374/e59535fa-0784-1c08-2ff5-88a50376bf98.png)

上記のようになっていますね。

```php
use Stevebauman\Location\Facades\Location;

Location::get(“逆引きしたいIPアドレス”)
```

上記で簡単に住所情報を取得できそうです。
ただ、データが取得できない場合があるので、条件分岐をしっかり入れないとダメみたいです。
データがない場合は、$position = false になります。
次は、住所データをどこから取得するかの設定です。


<a id="#Chapter4"></a>
# 住所データの取得方法


IPアドレスから住所を取得するためには、住所データへのアクセスを必ずしないといけないです。
そのため、どこ経由から住所データを取得するかを選択できるみたいです。

取得するDriverを何にするかを設定するには、config/location.php から設定ができます。


```php
// インストール時の設定を表示しています
/*
|————————————————————————–
| Driver
|————————————————————————–
|
| The default driver you would like to use for location retrieval.
|
*/

// 向き先を変更することで取得方法を変更可能

‘driver’ => Stevebauman\\Location\\Drivers\\IpApi,
```

住所データを取得する方法として、大まかに2通りあるみたいです。
※実装では、住所データをアプリケーションに取り入れる方法を採用しました。

### APIを都度叩いて取得する方法

デフォルトの設定ではこちらになっているみたいです。

APIの場合、トークンを取得する必要があるみたいですので、
取得するAPIからトークン情報を取得して、envに設定が必要みたいです。

都度アクセスする必要がありますが、
実装としてはこちらの方が簡単そうですね。


### 住所データ自体をアプリケーションに取り入れる方法

住所データをアプリケーションに取り入れる方法を今回の実装では採用をしております。

採用理由としては、
アプリケーション内にデータベースを持つことで、API取得時のエラーケースを無くしたかったからです。
特にセキュリティ面です。
都度アクセスするということは、取得したIPアドレス情報を外部にリクエストする必要がありますので、
何か起こると少々面倒だなと感じたためです。

アプリケーション内に住所データを取得するためには、Maxmind 社から住所データをダウンロードする必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572374/25230758-6242-0b6a-7173-cc867a442dfb.png)

取得するためのフローは、パッケージに書かれていますね。

https://www.maxmind.com/en/geolite2/signup

上記から、maxmind アカウントを作って、ダウンロードする必要があります。

ちなみに、maxmind社から取得するデータは、**有料版 GeoIP2 と無料版の GetLite2** がありますので注意してください。


今回の実装では、無料版のGetLite2 を採用しています。 

有料版と 無料版の違いは、住所の正確性(精度)です。
有料版の方がIPアドレスから取得できる住所の精度は高いです。
ざっと色々な記事を調べたところ距離としては、約40km = 60km の誤差は出るみたいですね。。(ネット記事からなので、あくまで参考程度)

公式からは、国ごとに有料版との比較を調べられるのでこちらから確認した方が良いです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572374/0670abde-1de2-e4af-c5de-2c3920ec05cd.png)

https://www.maxmind.com/en/geoip2-city-accuracy-comparison

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572374/f15744a1-3405-5348-9ef4-7d211b4661ed.png)

Download GZIPをクリックしてファイルをダウンロードします。

データをダウンロード・解凍を終えたら、データをdatabaseディレクトリ配下に設置します。

my-laravel-app/database/maxmind/GeoLite2-City.mmdb

また、設置後、Driverを下記のように変更します。

config/location.php

```php
/*
|————————————————————————–
| Driver
|————————————————————————–
|
| The default driver you would like to use for location retrieval.
|
*/

‘driver’ => Stevebauman\Location\Drivers\MaxMind::class,
```

さて住所データの設定は終わりました。

実際にテストをしてみましょう。

<a id="#Chapter5"></a>
# IPアドレスから住所データを取得する


設定が一通り終わりましたので、IPアドレスを入れてみます。

Laravel のIPアドレスの取得方法として、

```php
$request->ip() 
```

上記で簡単に取得することができます。

なお、ローカル環境の場合は、適当なIPアドレスを入れてみてテストをします。
ローカル環境で取得したIPアドレスを入れた場合は、falseになるみたいですので注意が必要です。

https://whoer.net/

上記で今自分がいるIPアドレスが簡単に取得できるので上記からIPアドレスを入れてみましょう。

取得したデータ

```php
{"Stevebauman\\Location\\Position":{"ip":"61.122.112.97","countryName":"Japan","countryCode":"JP","regionCode":"13","regionName":"Tokyo","cityName":"Chiyoda","zipCode":"100-8111","isoCode":null,"postalCode":null,"latitude":"35.6906","longitude":"139.77","metroCode":null,"areaCode":"13","driver":"Stevebauman\\Location\\Drivers\\MaxMind"}}
```

住所データは取得できました。

取得した時のデータ実装例としては、下記のようにしました。

```php
use Stevebauman\Location\Facades\Location;

$position = Location::get($request->ip())

if($position){
// データがある場合
$position->countryName  // 国名 例だと Japan
$position->regionName  // 県名 例だと Tokyo
$position->cityName     // 区 or 市 名  例だと Chiyoda

}else{
// データがない場合の処理

}
```

<a id="#Chapter6"></a>
# maxmind からAPIを叩いて、住所データをダウンロードする方法

先ほどは、手動でダウンロードしてdatabase配下にデータを設置しました。
ただ、この住所データは、1週間に1回更新されますので都度アップデートする必要があります。(毎週火曜更新 ※ maxmind がアメリカなので、アメリカ時間軸でだと思います)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572374/aa838f56-5d15-5e1a-9519-f95febadfa67.png)


常に最新情報を取得しない場合は、特に更新は不要で良いと思いますが、
更新する場合は、1週間に1回都度更新するのも手間がかかります。
その場合、APIを叩いて取得するようにしましょう。
住所データをダウンロードしたページを見ると、Get Permalinks を記載がありますね。
この中身を見るとダウンロード可能なAPIが表示されますので、そのAPIを叩けばデータをダウンロードすることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572374/4a11fee4-702c-8e3e-0fcd-fd1e733ff501.png)


APIは、アカウント登録しましたら確認をしてください。
APIを叩くためにはライセンスキーが必要となりますので、maxmind ページ内で登録する必要があります。

今回の実装では、次のように実装を行いました。

- 1週間に 1回 Task Schedulerを走らせてデータをダウンロードし、AWS S3にアップロード
- ダウンロードする際に、SHA256のAPIも一緒に叩き、ファイル改竄がないかをチェック
- storage配下にデータを置いて、storageから住所データを読み取る(config/location.php の設定を変更する必要あり)
- AWS コンテナ上でDEV環境以上は構築していたので、IPアドレスから住所を逆引きする際は、storage配下にデータがあるかチェック(コンテナが立て直されるとstorageから消えるため)
- なければS3からダウンロードする
- また、逆引きする際に、データの更新日時が古いようであればS3から最新データをダウンロードする

大まかにですが、上記で実装をしております。

<a id="#Chapter7"></a>
# まとめ

Laravel で実装を行なった記事がなく、実装で手こずりました。
より良い方法があれば、ブラッシュアップしていきたいです。

ここまでお読みいただきありがとうござました。✌︎('ω')✌︎

<a id="#reference"></a>
# 参考文献
- [https://github.com/stevebauman/location](https://github.com/stevebauman/location)