最終更新日:2020/03/22 原本2010-03-17

GlassFishからアプローチするJava~入門編~
第7回「Webアプリケーションの作成 JDBCレルムで認証実現」

複数のユーザが同時に買い物ができるように実装する

ダウンロード サンプルコード (17.6 KB)

目次

ログインJSPを作成

 図14のlogin.jspを見る限り何の変哲もないようですが、注意すべきことがあります。前ページで説明したように、url-patternで「/*」を指定しました。このコンテキスト内のリソースにアクセスすることで、当画面が表示され認証を行います。formタグのaction属性のj_security_checkはGlassFish内のWebコンポーネントなので、ある画面に直接アクセスしたくてURLを入力し目的の画面を表示しようとしても、必ず認証画面が表示されます。従って、j_usernameやj_passwordがアクセスしたいWebコンポーネントに渡ることはありません。図15がログイン画面です。

図15.ログイン画面
図15.ログイン画面

ServletでPrincipalからユーザー名を取得

 この部分こそ、Servletの基礎がないと実装に困るところです。筆者がよく見かけるのは、JSP内でパラメタのj_usernameを捕捉しようと頑張ったり、ユーザー名を取り出すためだけにCustomレルムを作ろうと奮闘している姿です。Customレルムの場合、レルムを理解するのに非常に勉強になるため時間があれば黙って見守ることにしています。ネットでもレルムを使うとユーザー名を簡単に取り出せないので使えないなどという投稿があったりしますが、あまりにも基礎的なことのためか回答がありません。

 JAASのAPIがあるにもかかわらずレルムを使うのが一般的になってきたのは、定義とプログラムが分離できるという大きなメリットがあるためだと考えられます。JAASの基本的なことはレルムでできてしまうため、JAASを直接使わなくなりつつあるのもうなずけます。今回は、連載第2回のServletの説明がマルチスレッド問題に絞っていたため、ほとんどServletのAPIには触れませんでした。従って、ServletのAPIでレルムで認証された者(表1のユーザーで触れたprincipal)を使うという方法が分からないのも当然です。principalはjava.security.PrincipalというインターフェースでgetNameというメソッドを持っています。Principal自体はrequest、つまり普段何気なく使っているprocessRequestの引数HttpServletRequestから取得できます。以上をまとめると次のようにコーディングすれば、簡単にユーザー名が取得可能です。

String userId = request.getUserPrincipal().getName();

 どうでしょうか。JSPやJavaScriptを書きまくるでもなく、大がかりなCustomレルムを作ることもなく、簡単にレルムで認証されたユーザー(principal)の名前を取得できてしまいました。また、

request.isUserInRole("user")

 はロールが引数と等しいかを判定し、等しい場合trueを返します。今回の例では、引数が「user」なので、「tomoharu」や「ichiro」はtrueが返り、「jiro」はfalseが返ります。このHttpServletRequestは非常に役立つメソッドを多く持っています。すぐにでもAPIを調べて使えそうなメソッドを確認することをお薦めします。以下は、ログイン画面から呼び出されるImoshochuCatalogServletクラスのprocessRequestメソッドです。

ImoshochuCatalogServletクラスのprocessRequestメソッド
001:    protected void processRequest(HttpServletRequest request,
002:                                    HttpServletResponse response)
003:    throws ServletException, IOException {
004:        // DAOクラスをインスタンス化する
005:        ImoshochuCatalogDAO imoshochuCatalogDAO = new ImoshochuCatalogDAOImpl();
006:        // requestからHttpSessionを取り出し、ImoshochuCatalogItemのArrayList
007:        // を"imoCatalog"として埋め込む
008:        request.getSession().setAttribute("imoCatalog",
009:                                imoshochuCatalogDAO.getImoshochuCatalogList());
010:        // 第7回で追加
011:        String userId = request.getUserPrincipal().getName();
012:        BasketHelper basketHelper = new BasketHelper();
013:
014:        int basketId = 0;
015:        ArrayList basketItems = new ArrayList();
016:        // getBasketIdのパラメタを定数「tomoharu」から変数userIdに変更
017:        basketId = basketHelper.getBasketId(userId);
018:        basketItems = basketHelper.getBasketItems(basketId);
019:        request.getSession().setAttribute("basket", basketItems);
020:        // 第6回で追加
021:        ArrayList totalCharge = basketHelper.getTotalCharge(basketItems);
022:        request.getSession().setAttribute("totalCharge", totalCharge);
023:
024:        // ImoshochuShop.jspに遷移する
025:        RequestDispatcher rd = request.getRequestDispatcher("/ImoshochuShop.jsp");
026:        rd.forward(request, response);
027:
028:    } 

 11行目が先述のユーザ名を取得するためのコードです。連載第5回第6回は17行目のuserIdに固定の値「tomoharu」を設定していましたが、今回は11行目で取得したuserIdを設定するよう変更しています。他のServletのBasketServlet、RefreshBasketServlet、RemoveBasketServletも同様の変更を加えています。サンプルコードで確認してください。

当記事の注意点

 前ページのコラム「※注意」でも説明したように、トランスポート層はSSLで暗号化しなければなりません。また、最低でもパスワードも暗号化し、データベースに格納することがJDBCレルムの要件となっています。ほとんどのデータベース製品が暗号化するための関数を持っています。

 今回使用しているMySQLもMD5関数を持っており、32ビットのハッシュ値を生成してくれます。また、web.xmlのauth-method要素でDIGESTと指定するとパスワードのハッシュ値が生成されます。ただ、SSLで通信路を暗号化しないとExceptionが発生し認証に失敗します。企業レベル、あるいは今回のサンプルアプリケーションのようなネットショップではSSL抜きの通信は今では考えられません。RSAなど有料といえども信頼できる製品を選択すべきです。

サンプルコードについて

 図16のファイルは全て納められています。データベース[Workbench]-[Server Administration]-[Manage Import / Export]をクリックすると[Import/Export MySQL Data]というダイアログボックスが表示されます。[imoshop]を選択し、[OK]ボタンをクリックすると新しい画面が表示され、その中の[Data Dump]が選択されているはずです。左ペインの[Import from Disk]をクリックします。左ペインに2つのラジオボタンがあり[Import from Self-Contained File]をチェックし、[File Path]にダウンロードしたサンプルコードに含まれるDump20100221-2.sqlを指定します。右隅にある[Start Import]ボタンをクリックし、左側にあるプログレスバーが右まで埋まればデータベース構築が完了します。他のサンプルコードは図16のように配置してご利用ください。

図16.入門編最終回のプロジェクトのフォルダ構成
図16.入門編最終回のプロジェクトのフォルダ構成

 入門編は当記事が最終回となります。最後までお付き合い頂いた皆さまには感謝の限りです。応用編またはそれに類するような企画で、さらにWebアプリケーションの世界、あるいはJava EEの世界を探検できればと思っています。

バックナンバー

連載:GlassFishからアプローチするJava