TomcatのServletでクライアントIPを取得すると 127.0.0.1 になる現象

TomcatのServletでクライアントIPを取得するために HttpServletRequest の getRemoteAddr() メソッドを使っているのですが、取得するIPがいつも 127.0.0.1 となってしまうので調べてみました。環境は以下の構成です。NginxとTomcatは相乗りです。

ServletでのクライアントIP取得のソースコードです。


//IPアドレスの取得
String ip = request.getHeader("X-FORWARDED-FOR");
if ( ip == null || "".equals(ip) )
	ip = request.getRemoteAddr();
if ( ip == null )
	ip = "0.0.0.0";

このロジックだと request.getRemoteAddr(); がいつも 127.0.0.1 になってしまいます。ループバックアドレスなので自身のIPアドレスとなっているようです。つまり、TomcatからみたクライアントはNginxとなっています。

これを解消するにはNginx側の設定ファイルに proxy_set_header ディレクティブの追加が必要だとわかりました。以下のようにします。


location / {
	 :
	proxy_set_header X-Forwarded-for $remote_addr;
	proxy_pass http://localhost:8080/test/;
	 :
}

Nginxはデフォルトでは X-Forwarded-For ヘッダを付けてくれないので、付加する設定がいるようです。Servlet側でも request.getHeader("X-FORWARDED-FOR"); の処理を入れておく必要があります。こうするとクライアントIPの取得ができました。

X-Forwarded-For ヘッダはプロキシサーバーを通過した際に、送信元IPアドレスを特定するために使われます。HTTPヘッダには以下のような形で付与されます。

X-Forwarded-For: <client>, <proxy1>, <proxy2>

リクエストが複数のプロキシサーバーを通過する場合、それぞれの通過するプロキシサーバーのIPアドレスが右側に付け足されていきます。つまり、左端のIPアドレスが元のクライアントのIPアドレスになります。

なお、1つ手前のプロキシサーバー(一番最後に経由したプロキシサーバー)のIPアドレスは X-Forwarded-For ヘッダには記載されないので そのIPアドレスを取得するには request.getRemoteAddr() メソッドで取得します。

Javaでパッケージにしたクラスのコンパイルと実行方法

任意のパッケージにあるクラスのコンパイルと実行方法についてです。環境はLinuxです。

ソースファイル(Main.java)


package sample01;

class Main {

	public static void main( String[] args ) {
	
		Number num = new Number();
		System.out.println( "Number is " + num.getNum() + "." );

	}
}

ソースファイル(Number.java)


package sample01;

public class Number {

	private int num;

	public Number() {
		num = 10;
	}

	public int getNum() {
		return num;
	}
}

MainクラスとNumberクラスはsample01というパッケージに属しています。

これらのソースを配置するのですが、ディレクトリ構成は以下のようにします。Javaフォルダは任意の場所に配置してください。

Javaフォルダにsrcフォルダとclassesフォルダを用意します。srcフォルダにsample01フォルダを作成し Main.java と Number.java を格納します。

コンパイルするためにコンパイル用のシェルを作成します。シェル名は compile.sh とします。compile.sh をsrcフォルダに格納します。実行権限もつけてください。

compile.shの内容


#!/bin/bash
javac -d ../classes -sourcepath ./ $1

compile.sh はカレントディレクトリでの実行とします。javacコマンドの説明をしておくと「-d . ./classes」はクラスファイルの出力先の指定、「-sourcepath ./」はソースファイルの検索場所となります。「$1」は compile.sh の引数です。

srcフォルダで compile.sh を実行します。引数は sample01/Main.java となります。

$ cd ./Java/src
$ ./compile.sh sample01/Main.java

compile.sh を実行すると /Java/classes/sample01 フォルダに Main.class と Number.class が生成されます。

Main.class を実行するにはclassesフォルダでjavaコマンドを実行します。引数は sample01.Main となります。

$ cd ./Java/classes
$ java sample01.Main

「Number is 10.」と表示されれば実行できています。