やってみようpart6(JDBCRealm)

先に書いたとおり、認証はSAStrutsのサンプルでもあるとおりJ2EEコンテナにまかせることにしました。id:higayasuoさんおすすめらしいです。

テーブルの準備

まず、DBの準備です。ユーザテーブルにはユーザIDとクレデンシャル(パスワード)、ロールテーブルにはユーザIDとロール名があることが大前提のようです。

こまかいことを言うと、ユーザテーブルのキーはidなので、ロールテーブルにもidを外部キーとして持ちたい気分になりますが、そこはそれ、画面から入力される文字列がここでいうIDになるので、ここではlogin_idをロールテーブルに外部キーとして持たせています。

server.xmlの設定

えーっと、Tomcat前提です。
すでにserver.xmlにJDBCRealmの設定がコメントされていますのでそれ参考で。以下抜粋です。
上のテーブル構成だとこんな感じになりました。

  <Realm className="org.apache.catalina.realm.JDBCRealm"
         connectionName="dbuser"
         connectionPassword="dbpass"
         connectionURL="jdbc:mysql://hostname/dbname"
         driverName="com.mysql.jdbc.Driver"
         roleNameCol="role"
         userCredCol="passwd"
         userNameCol="login_id"
         userRoleTable="roles"
         userTable="users"
  />

サンプルだとドライバーのクラスが org.gjt.mm.mysql.Driver になっていますがどうなんでしょう。ぼくはjdbc.diconにあわせました。ちゃんと動いてるからこれでいいんでしょう。

追記:
デフォルトで有効になっているUserDatabaseRealmはコメントアウトしましょう。(2008.6.26)

	  <!-- コメントアウトしましょう
      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
             resourceName="UserDatabase"/>
		-->

web.xmlの設定

基本的にはSAStrutsチュートリアルのまんまです。

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>
            Authentication
            </web-resource-name>
            <url-pattern>/schedule/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
            <role-name>admin</role-name>
        </auth-constraint>
    </security-constraint>
    
    <security-role>
        <role-name>user</role-name>
    </security-role>
    <security-role>
        <role-name>admin</role-name>
    </security-role>
    
    <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
            <form-login-page>/WEB-INF/view/login/login.html</form-login-page>
            <form-error-page>/WEB-INF/view/login/loginError.html</form-error-page>
        </form-login-config>
    </login-config>

です。
おおきく、

にわけることができます。を一番最初に説明するのがよさそう。

    • このアプリケーションで有効にするロールの定義。つまりはロールテーブルのroleカラムにはいる値そのもの。ちなみに、どうしたって
    <!-- NG -->
    <security-role>
        <role-name>user</role-name>
        <role-name>admin</role-name>
    </security-role>

こう書きたくなるんだけど、これはNGのようです。

    • アクセス制限対象URL(複数可)
    • アクセスが許可されるロール(複数可)

現段階ではまだ設定してないですが、実際には、/manage/*以下はadminロール、/schedule/*以下はadmin or userとしたいので、そのものを複数指定してやればいいことになります。

認証方法(FORM)と認証のかかっているURLにアクセス時に遷移する画面()と認証エラー時に遷移する画面()を指定します。チュートリアルのままです。WEB-INF以下ですが、HTMLでもよいみたいです。

認証方法をBASICにしたら、そもそも認証画面とかエラー画面の指定はいらないような気がしてきた。まあいいや。

その認証画面

で指定した画面。formのaction属性とログインIDとパスワードのname属性は指定があります。

url /j_security_check
ログインIDのname属性 j_username
パスワードのname属性 j_password

以下、抜粋。

<form method="post" action="../j_security_check">
<table class="login">
<tr>
  <th>ログインID</th>
  <td><input type="text" name="j_username" value="" size="10" /></td>
</tr>
<tr>
  <th>パスワード</th>
  <td><input type="password" name="j_password" value="" size="10" /></td>
</tr>
<tr>
  <td colspan="2" style="text-align:center; padding-top:15px">
    <input type="submit" name="login" value="ログインする" />
  </td>
</tr>
</table>
</form>

認証成功

認証に成功したら「誰がログインしたのか」をアプリが認識する必要があります。
当たり前ですが、servlet APIに準備されてます。

    @Execute(validator = false)
    public String index() {
        Principal userPrincipal = request.getUserPrincipal();
        logger.debug("LoginUser is " + userPrincipal.getName());
        User user = userService.getUserDto(userPrincipal.getName());
        Beans.copy(user, userDto).execute();
        :
    }

って感じです。
よくよく考えるとこのweb.xmlの設定では、/schedule/ 以下だとなんでもかんでも認証がかかるわけなので、index()だけにユーザ情報取得処理をいれててもあかんのか。/schedule/hogeにアクセスされるとhoge()にいっちゃう?

それこそAOPで横断的になんとかできないかな。

今日はここまで。

追記:
http://d.hatena.ne.jp/ngtn/20080603/1212503018
こちらにも詳しく書かれています。
(2008.6.26)