2011年11月12日土曜日

Tomcat 6(Windows X64版)  認証用レルムの作成

Tomcatで使用する認証レルムを作成します。
今回は、簡単なJDBCレルムに相当するJAVAクラスを作成して試してみます。

①開発環境(Eclipse)において、以下のJARファイルをビルドパスに通します。
・catalina.jar(C:\Program Files\Apache Software Foundation\Tomcat 6.0\lib配下)
・tomcat-juli.jar(C:\Program Files\Apache Software Foundation\Tomcat 6.0\bin配下)

②以下のようなJDBCレルムもどきのクラスを作成します。本来ならば、ユーザが入力したパスワードとMySQLのパスワードの比較処理において、ダイジェスト値を考慮したものにしないといけませんが、まずは平文パスワードの比較を試してみます。ポイントは以下です。
◎org.apache.catalina.realm.RealmBase抽象クラスを継承します。
◎server.xmlの<Realm>要素の属性として指定したものが、setXXXメソッドに渡ってきます。
(たとえば、connectionName属性の値は、setConnectionNameの引数に渡ってきます)
◎authenticateメソッドが認証処理を行うメインメソッドとなります。
◎getPassword、getPrincipal、getNameメソッドは必ず実装する必要があります。
--------------------------------------------------------------------------
package com.yosiyosi.realm;
import java.security.Principal;
import org.apache.catalina.realm.GenericPrincipal;
import org.apache.catalina.realm.RealmBase;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;


public class CustomJDBCRealm extends RealmBase {
private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
.getLog(CustomJDBCRealm.class);
private Connection dbConnection = null;
private PreparedStatement preparedAuthenticate = null;
private PreparedStatement preparedRoles = null;
protected String connectionURL = null;
protected String connectionName = null;
protected String connectionPassword = null;
protected String userTable = null;
protected String userNameCol = null;
protected String userCredCol = null;
protected String userRoleTable = null;
protected String roleNameCol = null;
protected String driverName = null;


/**
* JDBCドライバをセット

* @param driverName
*/
public void setDriverName(String driverName) {
this.driverName = driverName;
}


/**
* 接続先URLのセット

* @param connectionURL
*/
public void setConnectionURL(String connectionURL) {
this.connectionURL = connectionURL;
}


/**
* 接続用ユーザ名のセット

* @param connectionName
*/
public void setConnectionName(String connectionName) {
this.connectionName = connectionName;
}


/**
* 接続用パスワードのセット

* @param connectionPassword
*/
public void setConnectionPassword(String connectionPassword) {
this.connectionPassword = connectionPassword;
}


/**
* ユーザテーブル名のセット

* @param userTable
*/
public void setUserTable(String userTable) {
this.userTable = userTable;
}


/**
* ユーザテーブルのユーザカラム名のセット

* @param userNameCol
*/
public void setUserNameCol(String userNameCol) {
this.userNameCol = userNameCol;
}


/**
* ユーザテーブルのパスワードカラム名のセット

* @param userCredCol
*/
public void setUserCredCol(String userCredCol) {
this.userCredCol = userCredCol;
}


/**
* ロールテーブル名のセット

* @param userRoleTable
*/
public void setUserRoleTable(String userRoleTable) {
this.userRoleTable = userRoleTable;
}


/**
* ロールテーブルのロールカラム名のセット

* @param roleNameCol
*/
public void setRoleNameCol(String roleNameCol) {
this.roleNameCol = roleNameCol;
}


/**
* 認証に成功したら、Principalをかえす。 失敗したら、nullを返す
*/
@Override
public Principal authenticate(String user, String password) {
// DBのパスワードを取得
String dbPass = getPassword(user);


// DBのロールリストを取得
List<String> dbRole = getUserRoles(user);


log.info("username=" + user + " password=" + password);
log.info("DB password:" + dbPass);


// 平文パスワードの比較
if (dbPass != null && dbPass.equals(password)) {
return new GenericPrincipal(this, user, dbPass, dbRole);
}


return null;
}


/**
* 認証するユーザのパスワードをDB検索する。

* @param username
* @return パスワード
*/
public synchronized String getPassword(String username) {
try {
// DBコネクションが確立されているかをチェック
if (!checkConnection()) {
return null;
}
if (preparedAuthenticate == null) {
preparedAuthenticate = getPreparedPasswd(dbConnection);
}
// ユーザの検索
preparedAuthenticate.setString(1, username);
ResultSet rs1 = preparedAuthenticate.executeQuery();
if (rs1.next()) {
return rs1.getString(1).trim();
}
rs1.close();
return null;
} catch (SQLException ex) {
ex.printStackTrace();
close();
return null;
}
}


/**
* 認証するユーザのロールをDB検索する。

* @param username
* @return ロールのリスト
*/
public synchronized List<String> getUserRoles(String username) {
try {
// DBコネクションが確立されているかをチェック
if (!checkConnection())
return null;
if (preparedRoles == null) {
preparedRoles = getPreparedRoles(dbConnection);
}
// ロール検索
preparedRoles.clearParameters();
preparedRoles.setString(1, username);
ResultSet rs = preparedRoles.executeQuery();
List<String> vrol = new ArrayList<String>();
while (rs.next()) {
vrol.add(rs.getString(1).trim());
}
return vrol;
} catch (SQLException ex) {
ex.printStackTrace();
close();
}
return null;
}


/**
* パスワード検索用のSQL文の作成

* @param conn
* @return SQL文
* @throws SQLException
*/
protected PreparedStatement getPreparedPasswd(Connection conn)
throws SQLException {
String sql = "SELECT " + userCredCol + " FROM " + userTable + " WHERE "
+ userNameCol + " = ?";
return conn.prepareStatement(sql);
}


/**
* ロール検索用のSQL文の作成

* @param conn
* @return SQL文
* @throws SQLException
*/
protected PreparedStatement getPreparedRoles(Connection conn)
throws SQLException {
String sql = "SELECT " + roleNameCol + " FROM " + userRoleTable
+ " WHERE " + userNameCol + " = ?";
return conn.prepareStatement(sql);
}


/**
* DBへのコネクションが確立しているかをチェック

* @return コネクションが確立されていたらtrue
*/
private boolean checkConnection() {
try {
if ((dbConnection == null) || dbConnection.isClosed()) {
Class.forName(driverName);
dbConnection = DriverManager.getConnection(connectionURL,
connectionName, connectionPassword);
if (dbConnection == null || dbConnection.isClosed()) {
return false;
}
}
return true;
} catch (SQLException ex) {
ex.printStackTrace();
close();
return false;
} catch (ClassNotFoundException ex) {
throw new RuntimeException("CustomJDBCRealm.checkConnection: " + ex);
}
}


/**
* DB切断
*/
private void close() {
if (preparedRoles != null) {
try {
preparedRoles.close();
} catch (Throwable t) {
;
}
preparedRoles = null;
}
if (preparedAuthenticate != null) {
try {
preparedAuthenticate.close();
} catch (Throwable t) {
;
}
preparedAuthenticate = null;
}
if (dbConnection != null) {
try {
dbConnection.close();
} catch (Throwable t) {
;
}
dbConnection = null;
}
}


/**
* 主体者情報を取得
*/
protected Principal getPrincipal(String username) {
return (new GenericPrincipal(this, username, getPassword(username),
getUserRoles(username)));


}


@Override
protected String getName() {
// TODO Auto-generated method stub
return null;
}
}
----------------------------------------------------------------------------

③JAVAクラスを作成したら、JARファイルを作成します。作成したJARをC:\Program Files\Apache Software Foundation\Tomcat 6.0\libに格納します。

④server.xml(C:\Program Files\Apache Software Foundation\Tomcat 6.0\conf配下)を編集します。<Realm>要素のclassName属性をカスタマイズしたクラス名に修正します。

      <Realm className="com.yosiyosi.realm.CustomJDBCRealm"
             driverName="org.gjt.mm.mysql.Driver"
             connectionURL="jdbc:mysql://localhost:3306/bushido"
             userTable="auth_users" userNameCol="user_name" userCredCol="user_password"
             userRoleTable="auth_roles" roleNameCol="role_name"
             connectionName="root" connectionPassword="password"/>

⑤Tomcatを再起動します。
「http://127.0.0.1:8080/sampleapp/sample.html」にアクセスし、FORM認証が成功することを確認します。また、Tomcatのサーバログに、以下のログメッセージが表示されていることを確認し、カスタマイズしたJAVAクラスが使用されていることを確認します。
------------------------------------------------------------------
2011/11/12 23:05:44 com.yosiyosi.realm.CustomJDBCRealm authenticate
情報: username=hanako password=pass
2011/11/12 23:05:44 com.yosiyosi.realm.CustomJDBCRealm authenticate
情報: DB password:pass
------------------------------------------------------------------

0 件のコメント: