java ee detail of jdbc-realm

106
GlassFish v4 ではじめる Java EE JDBC レルム ハンズオン・ラボ Version 1.0 Yoshio Terada, Java Evangelist http://yoshio3.com, @yoshioterada

Upload: oracle-fusion-middleware

Post on 08-May-2015

12.861 views

Category:

Technology


3 download

DESCRIPTION

This contents explains the detail of JDBC realm.

TRANSCRIPT

Page 1: Java EE  Detail of JDBC-Realm

GlassFish v4 ではじめる Java EE JDBCレルム ハンズオン・ラボ Version 1.0

Yoshio Terada, Java Evangelist http://yoshio3.com, @yoshioterada

Page 2: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  2  

Table of Contents 1.0 はじめに  .............................................................................................................  3  

2.0 NetBeans と GlassFish の動作確認  ............................................................  6  

3.0 アプリケーションの設定  ................................................................................  11   3.1 : Maven プロジェクトの設定  ..............................................................................................  11   3.2 : JavaServer Faces の有効化  ............................................................................................  15   3.3 : Contexts and Dependency Injection の設定  ....................................................  18   3.4 : GlassFish のリクエスト・エンコーディングの修正  ............................................  21  

4.0 ユーザ・グループの登録・削除画面の作成  ...................................................  25   4.1 データベースにテーブルを作成  ..........................................................................................  26   4.2 JPA のエンティティを作成  ...................................................................................................  28   4.3 ユーザ管理用の Enterprise JavaBeans の作成  .......................................................  36   4.4 CDI (JSF 管理対象 Bean)の作成  .......................................................................................  42   4.5 ユーザ管理用Web ページ(JSF Facelets)の作成  .....................................................  49   4.6 ユーザ登録・削除アプリケーションの実行  ..................................................................  55  

5. アプリケーションに対するアクセス制限:認証・認可  ..................................  60   5.1 ログイン用の JSF ページと JSF 管理対象 Bean の作成  .......................................  62   5.2 GlassFish における JDBC レルムの設定と実装  .......................................................  72   5.3 認証設定、認証コードの実装、動作確認  .......................................................................  78   5.4 細かいカスタマイズ  ...................................................................................................................  88   5.5 処理単位でのアクセス制限方法  ..........................................................................................  97  

Appendix.1  .........................................................................................................  105   pom.xml  の設定内容  .........................................................................................................................  105  

Page 3: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  3  

1.0 はじめに Java EE では標準で認証・認可を扱うための機能が標準で備わっています。Java EE の各コンポーネントに対するセキュリティはコンテナ(Web コンテナ、EJB コンテナなど)で提供され、XML やアノテーションによる宣言的なセキュリティの他、プログラム上でセキュリティを実装することもできます。このハンズオン・ラボでは Java EE 6 の Servlet 3.0 で新しく追加された login, logout メソッドを JavaServer Faces 内で使用し、データベースのユーザ・テーブルに存在するユーザとの認証、グループ・テーブルに設定されたユーザの役割(ロール)に応じてアクセス権限を与える認可処理のそれぞれを実装します。   本ハンズオン・ラボは Java EE 7 環境上で実装しますが、Java EE 6 の環境(GlasFish v3.x)でも同様に実施することが可能です。

• Servlet 3.0 以上 • JavaServer Faces 2.0 以上 • Contexts and Dependency Injection 1.0 以上 • Java Persistence API 2.0 以上 • Enterprise JavaBeans 3.1 以上

本ハンズオン・ラボで作成する全ソースコードは下記より参照可能です。 https://github.com/yoshioterada/JDBC-Realm-Sample

図  1:認証アプリケーションの完成イメージ  

Page 4: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  4  

必須ソフトウェア

l 最新の JDK 7 を入手してください。 http://www.oracle.com/technetwork/java/javase/downloads/index.html

                                                                                   

図  2:JDKのダウンロード

Page 5: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  5  

l NetBeans 7.4 以降を入手してください。NetBeans のダウンロードサイトより「Java EE」もしくは「すべて」のパッケージを選択し「ダウンロード」ボタンを押下してください。「Java EE」、「すべて」を選択した場合、本ハンズ・オンの動作に必要なGlassFish v4 がバンドルされています。 http://netbeans.org/downloads/

 

図  3:NetBeans  のダウンロード画面

Page 6: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  6  

2.0 NetBeans と GlassFish の動作確認 NetBeans をインストールしたのち、アイコンをダブル・クリックしてNetBeans を起動してください。起動すると下記の画面が表示されます。

 ここで、NetBeans と GlassFish が正しく連携できているかを確認するため、新しくプロジェクトを作成してください。メニューから「ファイル (F)」→「新規プロジェクト(W)...」を選択してください。

図  4:NetBeans  の起動画面

図  5:新規プロジェクトの作成

Page 7: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  7  

「新規プロジェクト(W)...」を選択すると下記のウィンドウが表示されます。「カテゴリ(C) :」より「Maven」を選択し、「プロジェクト(P):」より「Webアプリケーション」を選択し「次へ >」ボタンを押下してください。

 ボタンを押下すると下記のウィンドウが表示されます。ここで「プロジェクト名(N) :」に「WebSocket-Mailer」と入力し、「グループ ID(G):」に「jp.co.oracle」と入力し「次 >」ボタンを押下してください。

図  6:新規  Maven  プロジェクト作成  

図  7:新規  Web  アプリケーション

Page 8: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  8  

「次 >」ボタンを押下すると下記のウィンドウが表示されます。「終了(F)」ボタンを押下してください。

終了ボタンを押下すると下記のウィンドウが表示されます。  

図  8:新規  Web  アプリケーション

図  9:新規プロジェクトの作成

Page 9: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  9  

ここで、プロジェクトを実行してください。「WebSocket-Mailder」プロジェクトを右クリックし、「実行」を選択してください。

もしくは、NetBeans のメニューより「実行ボタン」を押下してください。   プロジェクトを実行すると、ブラウザが自動的に起動され下記の画面が表示されます。下記の画面がブラウザ上で表示されている場合、アプリケーション開発環境が正しく構築されています。

図  10:プロジェクトの実行

図  11:プロジェクトの実行

Page 10: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  10  

  開発環境の動作確認ができましたので、次章より実際にアプリケーションの開発を始めていきます。

図  12:アプリケーションの動作確認

Page 11: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  11  

3.0 アプリケーションの設定  本章では、アプリケーションの各種設定を行います。

3.1 : Maven プロジェクトの設定 本プロジェクトはApache Maven のプロジェクトとして作成しました。Maven は Apache Ant に代わる、プロジェクト管理ツールとして幅広く採用されており、今回開発に使用するNetBeans をはじめ、他のメジャーな統合開発環境で利用できるようになっています。Maven プロジェクト1の構成を管理するためには、POM(Project Object Model)ファイルを編集して行います。 左ペインの「プロジェクト」より、「WebSocket-Mailer」→「プロジェクト・ファイル」→「pom.xml」を選択しダブルクリックしてください。

ファイルをダブル・クリックし選択すると、右ペインに下記のウィンドウが表示されます。

                                                                                                               1  本ハンズオンでは Apache Maven に関する説明は省略します。Maven に関する詳細を把握したい場合、別途 http://maven.apache.org/ より情報を入手してください。  2  ここでご紹介する内容は Java EE 6, Java EE 7 共通です。  3  以前は MD5 を指定する事がありましたが、今はセキュリティの観点で MD 5 は非推奨で、

図  13:pom.xml  の選択

Page 12: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  12  

ここで、pom.xml に対して下記の設定を追加してください。 本プロジェクトでは、PrimeFaces 4.0 で提供されている JSF コンポーネントを使用します。PrimeFaces 4.0 を利用できるように下記の設定を追加してください。 <repositories> <repository> <id>prime-repo</id> <name>PrimeFaces Maven Repository</name> <url>http://repository.primefaces.org</url> <layout>default</layout> </repository> </repositories> <dependencies> ……(既存の設定) <dependency> <groupId>org.primefaces</groupId> <artifactId>primefaces</artifactId> <version>4.0</version> </dependency> </dependencies>

図  14:デフォルトで作成される pom.xmlファイル

Page 13: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  13  

次に Java EE 7 の Web アプリケーション内で Concurrency Utilities for EE (Web Profile に含まれない機能)を利用できるように「artifactId」の箇所を「javaee-web-api」から「javaee-api」に修正してください。 <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> ※ 設定内容の詳細は「Appendix1 : pom.xml  の設定内容」をご参照ください。 pom.xml ファイルに対して設定を追加・編集を行なった後、ファイルを保存してください。NetBeans のメニューより「ファイル(F)」→「保存(S)」を選択してください。

図  15:pom.xmlファイルの保存

Page 14: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  14  

「図   16:依存性でビルド」に従いファイルを保存した後、「依存性でビルド」を実行してください。すると指定したMaven のリポジトリより必要なファイルを自動的に取得します。    

図  16:依存性でビルド

図  17:プロパティの編集  

Page 15: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  15  

3.2 : JavaServer Faces の有効化 次にプロジェクト内で JavaServer Faces を利用できるように、プロジェクトにフレームワークの追加を行ないます。「図  17:プロパティの編集」に従い「プロジェクト」→「WebSocket-Mailer」を選択した後、右クリックし「プロパティ」を選択してください。選択すると下記のウィンドウが表示されます。

ここで、「カテゴリ(C)」より「フレームワーク」を選択し、「追加(A)...」ボタンを押下してください。押下すると「フレームワークの追加」ウィンドウが表示されますので、ここで「JavaServer Faces」を選択し「OK」ボタンを押下してください。

図  18:プロジェクト・プロパティ設定画面

図  19: JavaServer  Facesの追加

Page 16: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  16  

ボタンを押下すると下記のウィンドウが表示されます。最後に「OK」ボタンを押下してください。 ボタンを押下するとプロジェクト内に「WEB-INF」ディレクトリ、「web.xml」ファイル、「index.xhtml」ファイルが自動的に追加されます。「web.xml」ファイルを選択しダブル・クリックすると下記の設定内容を確認できます。 ※今回作成するアプリケーションは、メール参照アプリケーションで長時間動作するアプリケーションのため、必要に応じて<session-timeout>の値を変更してください。デフォルトは 30分が指定されています。

図  20:プロジェクト・プロパティ

図  21:JSFフレームワーク追加と web.xmlファイル

Page 17: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  17  

ここで、プロジェクト作成時に自動生成された「index.html」ファイルは不要になりましたので削除してください。対象の「index.html」ファイルを選択し右クリックしてください。右クリックすると下記のようにメニューが表示されますので「削除 Delete」を選択してください。 選択すると下記の確認ダイアログが表示されますので、「はい」を押下してください。

図  22:index.html  ファイルの削除

図  23:index.htmlファイル削除確認ダイアログ

Page 18: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  18  

3.3 : Contexts and Dependency Injection の設定 次にContexts and Dependency Injection (移行 CDI)の設定を行ないます。Java EE 6 では、WEB-INF もしくは、META-INF ディレクトリ配下に 「beans.xml」と名付けられたファイルを作成することでCDI が有効になりました。Java EE 7 からは「beans.xml」ファイルを作成しなくてもデフォルトでCDI が有効になりました。しかし Java EE 7 のデフォルトの設定(annotated)は、@Namedのアノテーションが付加されたCDI のみがインジェクション可能で、@Named以外のアノテーションが利用不可となっています。そこで@Named以外のアノテーションでもインジェクションができるようにデフォルトの設定を変更します。 プロジェクトより「WebSocket-Mailer」→「新規」→「その他...」を選択してください。選択すると下記のウィンドウが表示されます。                                

         

図  24:CDI  のデフォルト設定を変更  

Page 19: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  19  

ここで、「カテゴリ(C)」より「コンテキストと依存性」を選択し、「ファイル・タイプ(F):」より「beans.xml (CDI 構成ファイル)」を選択し「次 >」ボタンを押下してください。                                     ボタンを押下すると下記のウィンドウが表示されます。ここではそのまま「終了(F)」ボタンを押下してください。                                          

図  25:beans.xml構成ファイル作成  

図  26:beans.xml構成ファイルの作成  

Page 20: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  20  

「終了(F)」ボタンを押下すると下記のファイルが作成されます。 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" bean-discovery-mode="annotated"> </beans> ここで「beans-discovery-mode」の値を「annotated」から「all」に修正してください。 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" bean-discovery-mode="all"> </beans> 修正した後、ファイルを保存してください。NetBeans のメニューより「ファイル(F)」→「保存(S)」を選択してください。                            

図  27:beans.xmlの設定を保存

Page 21: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  21  

3.4 : GlassFish のリクエスト・エンコーディングの修正 次にGlassFish のリクエスト・エンコーディングの修正を行ないます。GlassFish はパラメータのエンコーディングにデフォルトで ISO-8859-1 が使用されます。そこで日本語環境においては文字化けが発生しますので、パラメータのエンコーディングを ISO-8859-1 からUTF-8 に変更します。 プロジェクトより「WebSocket-Mailer」→「Web ページ」→「WEB-INF」ディレクトリを選択し右クリックした後「その他...」を選択してください。

 

 

 

 

 

 

 

 

 選択すると下記のウィンドウが表示されます。ここで「カテゴリ(C):」より「GlassFish」を選択し「ファイル・タイプ(F):」より「GlassFish ディスクリプタ」を選択し「次 >」ボタンを押下してください。                                      

図  28:GlassFishのデフォルト・エンコーディングの修正

図  29:GlassFishディスクリプタの作成  

Page 22: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  22  

ボタンを押下すると下記のウィンドウが表示されます。そのまま「終了(F)」ボタンを押下してください。 終了ボタンを押下すると下記の画面が表示されます。ここで「XML」ボタンを押下してください。

図  30:GlassFIshディスクリプタの作成

図  31:GlassFishディスクリプタの作成

Page 23: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  23  

「XML」ボタンを押下すると下記の画面が表示されます。    ここで、設定ファイル内に「<parameter-encoding default-charset="UTF-8" />」の1行を追加してください。  <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd"> <glassfish-web-app error-url=""> <class-loader delegate="true"/> <jsp-config> <property name="keepgenerated" value="true"> <description>Keep a copy of the generated servlet class' java code.</description> </property> </jsp-config> <parameter-encoding default-charset="UTF-8" /> </glassfish-web-app>    

図  32:glassfish-­‐web.xmlの内容

Page 24: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  24  

修正した後、ファイルを保存してください。NetBeans のメニューより「ファイル(F)」→「保存(S)」を選択してください。                        

図  33:glassfish-­‐web.xmlファイルの保存

Page 25: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  25  

4.0 ユーザ・グループの登録・削除画面の作成 それでは、実際にアプリケーションの開発を行なっていきます。2 まず、最初に作成するWebアプリケーションは、ユーザとグループを登録・削除するアプリケーションです。「ユーザ情報登録画面」に「ユーザ名」、「メールアドレス」、「パスワード」、「所属グループ」を入力し「ユーザ登録」ボタンを押下し、データベースに情報を登録します。登録する情報は、事前にデータベース内に作成したテーブル「USERTABLE」、「GROUPTABLE」に対して行います。また、この際、登録画面に入力されたパスワードは、SHA-256 のメッセージ・ダイジェスト3に変換し保存します。 また「ユーザ情報削除画面」には登録した「ユーザ名」を指定し、「ユーザ削除」ボタンを押下すると該当するユーザを削除します。

                                                                                                               2  ここでご紹介する内容は Java EE 6, Java EE 7 共通です。  3  以前は MD5 を指定する事がありましたが、今はセキュリティの観点で MD 5 は非推奨で、SHA-256 が推奨です。より強度の高い Salted SHA(SSHA)を使用したい場合は、独自のカスタム・レルムを作成する必要があります。SSHA を利用する場合は下記の URL をご参照ください。http://blog.eisele.net/2012/07/glassfish-jdbc-security-with-salted.html

図  34:ユーザ情報登録・削除アプリケーション

Page 26: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  26  

4.1 データベースにテーブルを作成 まず、認証するユーザとユーザが持つ役割を保持するデータベースのテーブルを作成します。今回は GlassFish に組み込みの JavaDB (Derby) にテーブルを作成します。JavaDB 付属コマンドである ij コマンドを使用して作成するか、もしくはNetBeans の「コマンドの実行 …」からコマンドを実行することもできます。 今回、簡単に実行するため、NetBeans から SQL コマンドを実行します。 「サービス」タブから「データベース」→「jdbc:derby://localhost:1527/sample」を選択しデフォルトで作成されているデータベース「APP」を選択し右クリックしてください。 右クリックすると上記のメニューが表示されます。ここで「コマンドの実行…」を選択してください。選択すると下記の画面が表示されます。

図  35:NetBeans  から SQLコマンドの実行  

図  36:NetBeansから SQL  コマンドの実行  

Page 27: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  27  

元々何も入力されていません。そこで下記SQL文を入力して画面右上部にある「SQLの実行(メタ+Shift+E)」ボタンを押下してください。 create table usertable ( username varchar(128) NOT NULL CONSTRAINT USER_PK PRIMARY KEY , mailaddress varchar(128) NOT NULL, password varchar(128) NOT NULL ); create table grouptable( username varchar(128) NOT NULL, groupid varchar(128) NOT NULL, CONSTRAINT GROUP_PK PRIMARY KEY(username, groupid), CONSTRAINT USER_FK FOREIGN KEY(username) REFERENCES usertable(username) ON DELETE CASCADE ON UPDATE RESTRICT ); SQL が正しく実行されると「出力」に下記のメッセージが出力されます。 0.032 秒で実行が成功しました。0 行が変更されました。 1 行目 2 文字目 0.022 秒で実行が成功しました。0 行が変更されました。 8 行目 2 文字目 0.054 秒後に実行が終了しました。0 個のエラーが発生しました。 SQL を実行した後、データベースの「表」を「リフレッシュ」し更新してください。すると下記のように「USERTABLE」、「GROUPTABLE」が作成されていることを確認できます。

図  37:データベース上に作成されたテーブルの確認  

Page 28: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  28  

ユーザとグループ情報を登録するための、テーブルを作成しましたので、次に実際にユーザとグループのテーブルに情報を登録します。今回はユーザとグループに情報を登録する事だけが目的のため、登録時、削除時の確認画面や、ユーザに対する複数グループの登録機能は省略します。(※ DBテーブルは複数グループの登録に対応しています。)

4.2 JPAのエンティティを作成 今回データベースにユーザ情報、グループ情報を登録・削除するため Java EE 標準のORMフレームワークである Java Persistence API を使用します。既にデータベースのテーブルが存在していますので、データベースのテーブルから JPA の Entity クラスを自動作成してください。「WebSocket-Mailer」プロジェクトを選択し右クリックしてください。右クリックするとメニューが表示されますので、「新規」→「その他...」を選択してください。 その他を選択すると下記のウィンドウが表示されます。ここで「カテゴリ(C):」より「持続性」を選択し、「ファイル・タイプ(F):」より「データベースからのエンティティ・クラス」を選択し「次 >」ボタンを押下してください。

図  38:データベースからエンティティの作成

Page 29: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  29  

ボタンを押下すると下記の画面が表示されます。ここで「データ・ソース(D):」より「jdbc/__default」を選択し「使用可能な表(T):」より「GROUPTABLE」を選択し「追加(A) >」ボタンを押下してください。ボタンを押下すると「選択した表(E):」に「GROUPTABLE」と「USERTABLE」が追加されます。2つの表が選択されている事を確認し「次 >」ボタンを押下してください。

図  39:データベースからのエンティティ・クラスの生成

図  40:データベースからのエンティティ・クラスの生成

Page 30: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  30  

ボタンを押下すると下記の画面が表示されます。ここで、「パッケージ(K):」に「jp.co.oracle.websocket.mailer.entities」と入力し「JAXB注釈を生成(J):」のチェックを外し「次 >」ボタンを押下してください。 ボタンを押下すると下記の画面が表示されます。ここではデフォルトの設定のまま最後に「終了(F)」ボタンを押下してください。

図  41:データベースからのエンティティ・クラスの生成

図  42:データベースからのエンティティ・クラスの生成

Page 31: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  31  

自動生成されたエンティティ・クラスは「Usertable.java」、「Grouptable.java」、「GrouptablePK.java」の3クラスで、それぞれ下記のコードになります。 package jp.co.oracle.websocket.mailer.entities; import java.io.Serializable; import java.util.Collection; import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; /** * * @author Yoshio Terada */ @Entity @Table(name = "USERTABLE") @NamedQueries({ @NamedQuery(name = "Usertable.findAll", query = "SELECT u FROM Usertable u"), @NamedQuery(name = "Usertable.findByUsername", query = "SELECT u FROM Usertable u WHERE u.username = :username"), @NamedQuery(name = "Usertable.findByMailaddress", query = "SELECT u FROM Usertable u WHERE u.mailaddress = :mailaddress"), @NamedQuery(name = "Usertable.findByPassword", query = "SELECT u FROM Usertable u WHERE u.password = :password")}) public class Usertable implements Serializable { private static final long serialVersionUID = 1L; @Id @Basic(optional = false) @NotNull @Size(min = 1, max = 128) @Column(name = "USERNAME") private String username; @Basic(optional = false) @NotNull

図  43:自動生成されたエンティティ・クラス

Page 32: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  32  

@Size(min = 1, max = 128) @Column(name = "MAILADDRESS") private String mailaddress; @Basic(optional = false) @NotNull @Size(min = 1, max = 128) @Column(name = "PASSWORD") private String password; @OneToMany(cascade = CascadeType.ALL, mappedBy = "usertable") private Collection<Grouptable> grouptableCollection; public Usertable() { } public Usertable(String username) { this.username = username; } public Usertable(String username, String mailaddress, String password) { this.username = username; this.mailaddress = mailaddress; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getMailaddress() { return mailaddress; } public void setMailaddress(String mailaddress) { this.mailaddress = mailaddress; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Collection<Grouptable> getGrouptableCollection() { return grouptableCollection; } public void setGrouptableCollection(Collection<Grouptable> grouptableCollection) { this.grouptableCollection = grouptableCollection; } @Override public int hashCode() { int hash = 0; hash += (username != null ? username.hashCode() : 0); return hash;

Page 33: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  33  

} @Override public boolean equals(Object object) { // TODO: Warning - this method won't work in the case the id fields are not set if (!(object instanceof Usertable)) { return false; } Usertable other = (Usertable) object; if ((this.username == null && other.username != null) || (this.username != null && !this.username.equals(other.username))) { return false; } return true; } @Override public String toString() { return "jp.co.oracle.websocket.mailer.entities.Usertable[ username=" + username + " ]"; } }

package jp.co.oracle.websocket.mailer.entities; import java.io.Serializable; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; /** * * @author Yoshio Terada */ @Entity @Table(name = "GROUPTABLE") @NamedQueries({ @NamedQuery(name = "Grouptable.findAll", query = "SELECT g FROM Grouptable g"), @NamedQuery(name = "Grouptable.findByUsername", query = "SELECT g FROM Grouptable g WHERE g.grouptablePK.username = :username"), @NamedQuery(name = "Grouptable.findByGroupid", query = "SELECT g FROM Grouptable g WHERE g.grouptablePK.groupid = :groupid")}) public class Grouptable implements Serializable { private static final long serialVersionUID = 1L; @EmbeddedId protected GrouptablePK grouptablePK; @JoinColumn(name = "USERNAME", referencedColumnName = "USERNAME", insertable = false, updatable = false) @ManyToOne(optional = false) private Usertable usertable; public Grouptable() { }

Page 34: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  34  

public Grouptable(GrouptablePK grouptablePK) { this.grouptablePK = grouptablePK; } public Grouptable(String username, String groupid) { this.grouptablePK = new GrouptablePK(username, groupid); } public GrouptablePK getGrouptablePK() { return grouptablePK; } public void setGrouptablePK(GrouptablePK grouptablePK) { this.grouptablePK = grouptablePK; } public Usertable getUsertable() { return usertable; } public void setUsertable(Usertable usertable) { this.usertable = usertable; } @Override public int hashCode() { int hash = 0; hash += (grouptablePK != null ? grouptablePK.hashCode() : 0); return hash; } @Override public boolean equals(Object object) { // TODO: Warning - this method won't work in the case the id fields are not set if (!(object instanceof Grouptable)) { return false; } Grouptable other = (Grouptable) object; if ((this.grouptablePK == null && other.grouptablePK != null) || (this.grouptablePK != null && !this.grouptablePK.equals(other.grouptablePK))) { return false; } return true; } @Override public String toString() { return "jp.co.oracle.websocket.mailer.entities.Grouptable[ grouptablePK=" + grouptablePK + " ]"; } }

package jp.co.oracle.websocket.mailer.entities; import java.io.Serializable; import javax.persistence.Basic; import javax.persistence.Column; import javax.persistence.Embeddable;

Page 35: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  35  

import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; /** * * @author Yoshio Terada */ @Embeddable public class GrouptablePK implements Serializable { @Basic(optional = false) @NotNull @Size(min = 1, max = 128) @Column(name = "USERNAME") private String username; @Basic(optional = false) @NotNull @Size(min = 1, max = 128) @Column(name = "GROUPID") private String groupid; public GrouptablePK() { } public GrouptablePK(String username, String groupid) { this.username = username; this.groupid = groupid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getGroupid() { return groupid; } public void setGroupid(String groupid) { this.groupid = groupid; } @Override public int hashCode() { int hash = 0; hash += (username != null ? username.hashCode() : 0); hash += (groupid != null ? groupid.hashCode() : 0); return hash; } @Override public boolean equals(Object object) { // TODO: Warning - this method won't work in the case the id fields are not set if (!(object instanceof GrouptablePK)) { return false; } GrouptablePK other = (GrouptablePK) object; if ((this.username == null && other.username != null) || (this.username != null && !this.username.equals(other.username))) { return false;

Page 36: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  36  

} if ((this.groupid == null && other.groupid != null) || (this.groupid != null && !this.groupid.equals(other.groupid))) { return false; } return true; } @Override public String toString() { return "jp.co.oracle.websocket.mailer.entities.GrouptablePK[ username=" + username + ", groupid=" + groupid + " ]"; } }

4.3 ユーザ管理用の Enterprise JavaBeans の作成 エンティティ・クラスを作成しましたので、次にユーザを管理(登録・削除)するためのビジネス・ロジックを Enterprise JavaBeans (EJB)として作成します。「WebSocket-Mailer」プロジェクトを選択し右クリックしてください。右クリックするとメニューが表示されますので、「新規」→「その他...」を選択してください。 選択すると下記のウィンドウが表示されます。ここで「カテゴリ(C):」より「エンタープライズ JavaBeans」を選択し「ファイル・タイプ(F):」より「セッションBean」を選択し「次 >」ボタンを押下してください。

図  44:Enterprise  JavaBeansの作成

Page 37: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  37  

ボタンを押下すると下記の画面が表示されます。ここで「EJB名(N):」に「UserRegistManager」と入力し「パッケージ(K):」に「jp.co.oracle.websocket.mailer.ejbs」を入力し最後に「終了(F)」ボタンを押下してください。

図  45:Enterprise  JavaBeansの作成

図  46:Enterprise  JavaBeansの作成

Page 38: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  38  

ボタンを押下すると下記の EJB クラスが自動的に生成されます。 package jp.co.oracle.websocket.mailer.ejbs; import javax.ejb.Stateless; /** * * @author Yoshio Terada */ @Stateless public class UserRegistManager { }

まず、ユーザをDBに登録するためのビジネス・ロジックを実装します。 ユーザ登録には、ユーザ名、メールアドレス、パスワード、グループ名を指定して行ないます。(※1ユーザに対して複数のグループを指定する事ができますが今回は単一グループに所属させる事にします。)下記の太字を実装してください。 @Stateless public class UserRegistManager { @PersistenceContext EntityManager em; /* 指定したユーザ、メールアドレス、パスワード、グループ名で DB へ登録 */ public void createUserAndGroup(String username, String mailaddress, String password, String groupname) { Usertable user = new Usertable(); user.setUsername(username); user.setMailaddress(mailaddress); SHA256Encoder encoder = new SHA256Encoder(); String encodedPassword = encoder.encodePassword(password); user.setPassword(encodedPassword); Grouptable group = new Grouptable(); group.setGrouptablePK(new GrouptablePK(username, groupname)); group.setUsertable(user); em.persist(user); em.persist(group); } } Usertable, Grouptable の各エンティティのインスタンスを生成しエンティティの各フィールドにそれぞれユーザ名、メールアドレス、パスワード、グループ名などを設定します。ここでパスワードの内容を平文ではなく、メッ

Page 39: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  39  

セージ・ダイジェスト(ハッシュ値)で保存するため、ダイジェスト・アルゴリズムとしてSHA-256 で生成します。 ここで、SHA-256 のダイジェストを生成するためのユーティリティ・クラスとして「SHA256Encoder」というクラスを生成してください。「WebSocket-Mailer」プロジェクトを選択し右クリックしてください。右クリックするとメニューが表示されますので、「新規」→「その他...」を選択してください。 選択すると下記のウィンドウが表示されます。ここで「カテゴリ(C):」より「Java」、「ファイル・タイプ(F):」より「Java クラス」を選択し「次 >」ボタンを押下してください。

図  47:SHA-­‐256エンコード・ユーティリティクラスの作成

図  48:SHA-­‐256エンコード・ユーティリティクラスの作成

Page 40: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  40  

ボタンを押下すると下記の画面が表示されます。ここで「クラス名(N):」に「SHA256Encoder」と入力し、「パッケージ(K):」に「jp.co.oracle.websocket.mailer.SHADigestUtil」と入力した後、最後に「終了(F)」ボタンを押下してください。 ボタンを押下すると Java のクラスが生成されます。そこでクラスに対して下記のメソッドを実装してください。 package jp.co.oracle.websocket.mailer.SHADigestUtil; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.logging.Level; import java.util.logging.Logger; /** * DB に保存するパスワードを SHA-256 の * メッセージ・ダイジェストとして保存 * プレイン・テキストとメッセージ・ダイジェストの変換ユーティリティ * * @author Yoshio Terada */ public class SHA256Encoder { // SHA-256 ハッシュとして保存 public String encodePassword(String origPassword){ String returnValue = ""; try { MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] digest = md.digest(origPassword.getBytes()); StringBuilder builder = new StringBuilder(); for (int i = 0; i < digest.length; i++) { String tmp = Integer.toHexString(digest[i] & 0xff); if (tmp.length() == 1) { builder.append('0').append(tmp); } else {

図  49:SHA-­‐256エンコード・ユーティリティクラスの作成

Page 41: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  41  

builder.append(tmp); } } returnValue = builder.toString(); } catch (NoSuchAlgorithmException ex) { Logger.getLogger(SHA256Encoder.class.getName()).log(Level.SEVERE, null, ex); } return returnValue; } }

次に、EJBに対してユーザ検索とユーザ削除を行なうためのロジッックを追加します。最終的な EJBのソースコードを下記に示します。 package jp.co.oracle.loginsample.ejbs; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import jp.co.oracle.loginsample.SHADigestUtil.SHA256Encoder; import jp.co.oracle.loginsample.entities.Grouptable; import jp.co.oracle.loginsample.entities.GrouptablePK; import jp.co.oracle.loginsample.entities.Usertable; /** * * @author Yoshio Terada */ @Stateless public class UserRegistManager { @PersistenceContext EntityManager em; /* 指定したユーザ、メールアドレス、パスワード、グループ名で DB へ登録 */ public void createUserAndGroup(String username,String mailaddress, String password, String groupname) { Usertable user = new Usertable(); user.setUsername(username); user.setMailaddress(mailaddress); SHA256Encoder encoder = new SHA256Encoder(); String encodedPassword = encoder.encodePassword(password); user.setPassword(encodedPassword); Grouptable group = new Grouptable(); group.setGrouptablePK(new GrouptablePK(username, groupname)); group.setUsertable(user); em.persist(user); em.persist(group); }

Page 42: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  42  

/* DB から指定したユーザの削除 */ public void removeUser(String username) { Usertable user = em.find(Usertable.class, username); em.remove(user); } /* DB から指定したユーザの検索 */ public Usertable findUser(String username){ Usertable user = em.find(Usertable.class, username); return user; } }

4.4 CDI (JSF 管理対象Bean)の作成 EJB のビジネス・ロジックを作成したので、JSF のバッキング・ビーンである CDI を作成します。ここでは管理用の画面からユーザ名、メールアドレス、パスワード、グループ名が入力される事を想定し、それぞれのフィールドを用意し、入力された値から EJBのビジネス・ロジックを呼び出すコードを生成します。 「WebSocket-Mailer」プロジェクトを選択し右クリックしてください。右クリックするとメニューが表示されますので、「新規」→「その他...」を選択してください。

図  50:JSF管理対象 Beanの作成

Page 43: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  43  

選択すると下記のウィンドウが表示されます。ここで「カテゴリ(C):」より「JavaServer Faces」、「ファイル・タイプ(F):」より「JSF 管理対象Bean」を選択し「次 >」ボタンを押下してください。 ボタンを押下すると下記の画面が表示されます。ここで「クラス名(N):」に「RegistPage」、「パッケージ(K):」に「jp.co.oracle.websocket.mailer.cdis」、「スコープ:」に「request」を選択し「終了(F)」ボタンを押下してください。

図  51: JSF  管理対象 Beanの作成

図  52: JSF管理対象 Bean  の作成

Page 44: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  44  

ボタンを押下すると下記のコードが生成されます。 package jp.co.oracle.websocket.mailer.cdis; import javax.inject.Named; import javax.enterprise.context.RequestScoped; import java.io.Serializable; /** * * @author Yoshio Terada */ @Named(value = "registPage") @SessionScoped public class RegistPage implements Serializable { /** * Creates a new instance of RegistPage */ public RegistPage() { } }

ここで、ユーザ名、メールアドレス、パスワード、グループ名が画面から入力される事を想定し各フィールドを用意します。下記 4つのフィールドを追加してください。 @Named(value = "regManage") @SessionScoped public class Regist implements Serializable { private String username; private String mailaddress; private String password; private String group; …… }

Page 45: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  45  

フィールドを追加した後、フィールドを隠蔽します。NetBeans のメニューより「リファクタリ...」を選択し「フィールドをカプセル化...」を選択してください。 プログラムを作成しユーザ情報、グループ情報を追加します。 まず、JPA の Entity を作成します。 選択すると下記のウィンドウが表示されます。ここで「取得メソッドを作成」、「設定メソッドを作成」の全チェックボックスにチェックし「リファクタリング(R)」ボタンを押下してください。 ボタンを押下すると各フィールド用のセッタ・ゲッタメソッドが自動的に作成されます。

図  53:フィールドのカプセル化

図  54:フィールドのカプセル化

Page 46: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  46  

次に、EJB (UserRegistManager)のビジネス・ロジック(createUserAndGroup)を呼び出しユーザ登録するメソッドを追加してください。ここでは@EJBのアノテーションを付加し、UserRegistManager の EJB をインジェクションしています。またWebページから入力された各情報を取得しcreateUserAndGroup()メソッドに値を渡しています。 @EJB UserRegistManager userRegist; /* DB へユーザ情報・グループ情報の登録 */ public void registDB() throws IOException { userRegist.createUserAndGroup( getUsername(), getMailaddress(), getPassword(), getGroup()); }

最終的に、登録用の JSF 管理対象 Bean の全ソースコードは下記になります。 package jp.co.oracle.websocket.mailer.cdis; import java.io.IOException; import javax.inject.Named; import javax.enterprise.context.RequestScoped; import java.io.Serializable; import javax.ejb.EJB; import jp.co.oracle.websocket.mailer.ejbs.UserRegistManager; /** * * @author Yoshio Terada */ @Named(value = "registPage") @RequestScoped public class RegistPage implements Serializable { private String username; private String mailaddress; private String password; private String group; @EJB UserRegistManager userRegist; /* DB へユーザ情報・グループ情報の登録 */ public String registDB() throws IOException { userRegist.createUserAndGroup(getUsername(), getMailaddress(), getPassword(), getGroup()); return "reg-success";

Page 47: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  47  

} /** * Creates a new instance of RegistPage */ public RegistPage() { } /** * @return the username */ public String getUsername() { return username; } /** * @param username the username to set */ public void setUsername(String username) { this.username = username; } /** * @return the mailaddress */ public String getMailaddress() { return mailaddress; } /** * @param mailaddress the mailaddress to set */ public void setMailaddress(String mailaddress) { this.mailaddress = mailaddress; } /** * @return the password */ public String getPassword() { return password; } /** * @param password the password to set */ public void setPassword(String password) { this.password = password; } /** * @return the group */ public String getGroup() { return group; } /** * @param group the group to set */ public void setGroup(String group) { this.group = group; }

Page 48: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  48  

}

同様に削除用の JSF 管理対象Bean を「RemovePage.java」として作成してください。削除用の全ソースコードは下記になります。 package jp.co.oracle.websocket.mailer.cdis; import javax.ejb.EJB; import javax.inject.Named; import javax.enterprise.context.RequestScoped; import jp.co.oracle.websocket.mailer.ejbs.UserRegistManager; /** * * @author Yoshio Terada */ @Named(value = "removePage") @RequestScoped public class RemovePage { private String username; @EJB UserRegistManager userManager; /* DB からユーザ情報の削除 */ public String removeDB(){ userManager.removeUser(getUsername()); return "rem-success"; } /** * Creates a new instance of RemovePage */ public RemovePage() { } /** * @return the username */ public String getUsername() { return username; } /** * @param username the username to set */ public void setUsername(String username) { this.username = username; } }

Page 49: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  49  

4.5 ユーザ管理用Webページ(JSF Facelets)の作成 JSF の管理対象Bean が作成されましたので最後にユーザ登録・削除用のWebページを作成します。管理用のWebページは「user-manage」ディレクトリ配下にそれぞれ「regist.xhtml」、「remove.xhtml」を作成します。 「WebSocket-Mailer」プロジェクト配下の「Webページ」を選択し右クリックしてください。右クリックするとメニューが表示されますので、「新規」→「その他...」を選択してください。 その他を選択すると下記のウィンドウが表示されます。ここで「カテゴリ(C):」より「その他」を選択し、「ファイル・タイプ(F):」より「フォルダ」を選択し「次 >」ボタンを押下してください。

図  55:管理用 Webページのディレクトリ構成

図  56:管理用 Webページ・フォルダの作成

図  57:管理用 Webページ・フォルダの作成

Page 50: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  50  

ボタンを押下すると下記の画面が表示されます。ここで「フォルダ名(N):」に「user-manage」と入力し「終了(F)」ボタンを押下してください。 ボタンを押下すると「Webページ」配下に「user-manage」ディレクトリが作成されます。 作成したディレクトリ配下に登録・削除用のWebページを作成します。

図  58:管理用 Webページ・フォルダの作成

図  59:管理用 Webページ・フォルダの作成

Page 51: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  51  

作成した「user-manage」ディレクトリを選択し右クリックしてください。右クリックするとメニューが表示されますので、「新規」→「その他...」を選択してください。 選択すると下記のウィンドウが表示されます。ここで「カテゴリ(C):」より「JavaServer Faces」を選択し、「ファイル・タイプ(F):」より「JSF ページ」を選択し「次 >」ボタンを押下してください。 ボタンを押下すると下記の画面が表示されます。ここで「ファイル名:」に「regist」と入力し、最後に「終了(F)」ボタンを押下してください。

図  60:登録用 JSFページの作成

図  61:登録用 jSFページの作成

Page 52: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  52  

ボタンを押下すると自動的に下記の Facelets が生成されます。 <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> <h:head> <title>Facelet Title</title> </h:head> <h:body> Hello from Facelets </h:body> </html>

regist.xhtml に対して下記のようにコードを修正してください。 <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:p="http://primefaces.org/ui"> <h:head> <title>ユーザ情報登録画面</title> </h:head> <h:body> <h:form> <center> <p:panel header="ユーザ情報登録画面" style="width: 320px"> <h:panelGrid columns="2" columnClasses="column" cellpadding="5"> <h:outputLabel value="ユーザ名: " style="font-size:14px;"/> <h:inputText id="userName" value="#{registPage.username}" style="font-size:14px;"/> <h:outputLabel value="メールアドレス: " style="font-

図  62:登録用 JSFページの作成

Page 53: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  53  

size:14px;"/> <h:inputText id="mailaddress" value="#{registPage.mailaddress}" style="font-size:14px;"/> <h:outputLabel value="パスワード: " style="font-size:14px;" /> <h:inputSecret id="password" value="#{registPage.password}" style="font-size:14px;"/> <h:outputLabel for="group" value="所属グループ" style="font-size:14px;"/> <h:inputText id="group" value="#{registPage.group}" required="true" style="font-size:14px;" /> </h:panelGrid> <h:commandButton value="ユーザ登録" style="font-size:14px;" action="#{registPage.registDB}"/> <p:messages id="messages" autoUpdate="true" closable="true" /> <h:link value="戻る" outcome="/faces/index.xhtml" /> </p:panel> </center> </h:form> </h:body> </html>

上記のコード内で次の EL式が定義されています。 l #{registPage.username} l #{registPage.mailaddress} l #{registPage.password} l #{registPage.group}

Webページで入力された値はCDI として実装したRegpage のクラスのフィールド username, mailaddress, password, group にそれぞれ代入されます。またボタンが押下された際に、action で指定した #{registPage.registDB}が呼び出され、Regpage クラスの registDB()メソッドが実行されます。registDB()メソッドの返り値であるString 型 (reg-success)は JSF で暗黙的に画面遷移先を表します(例:成功した場合 reg-success.xhtml へ自動的に遷移)。 「図  60:登録用 JSFページの作成」と同様の手順で「reg-success.xhtml」ファイルを作成してください。作成した後、下記のように修正してください。#{registPage.username}に入力されたユーザ名が表示されます。 <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> <h:head> <title>ユーザ情報登録画面完了</title> </h:head> <h:body> ユーザ情報 (#{registPage.username}) は正しく登録されました。 </h:body> </html>

Page 54: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  54  

登録用のWebページを作成しましたので、削除用のWebページも作成してください。「図  60:登録用 JSFページの作成」と同様の手順でユーザ削除用のページを「remove.xhtml」として作成してください。 <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:p="http://primefaces.org/ui"> <h:head> <title>ユーザ情報削除画面</title> </h:head> <h:body> <h:form> <center> <p:panel header="ユーザ情報削除画面" style="width: 320px"> <h:panelGrid columns="2" columnClasses="column" cellpadding="5"> <h:outputLabel value="削除ユーザ名: " style="font-size:14px;"/> <h:inputText id="userName" value="#{removeManage.username}" style="font-size:14px;"/> </h:panelGrid> <h:commandButton value="ユーザ削除" style="font-size:14px;" action="#{removeManage.removeDB}"/> <p:messages id="messages" autoUpdate="true" closable="true" /> <h:link value="戻る" outcome="/faces/login/index.xhtml" /> </p:panel> </center> </h:form> </h:body> </html>

最後に削除完了画面を作成してください。「図   60:登録用 JSF ページの作成」と同様の手順でユーザ削除完了用のページを「rem-success.xhtml」として作成してください。 <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> <h:head> <title>ユーザ情報削除画面完了</title> </h:head> <h:body> ユーザ情報 (#{removePage.username}) が正しく削除されました。 </h:body> </html>

以上で、ユーザ登録・削除用のWebアプリケーションは作成されました。

Page 55: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  55  

全てのページを作成すると「user-manage」ディレクトリ配下に4つのファイルが存在しています。 最後に便利のためにデフォルトで表示される「index.xhtml」ファイルを編集し作成したユーザ登録・削除用の画面へのリンクを記載してください。「index.xhtml」ファイルをダブル・クリックした後下記のように修正してください。 <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> <h:head> <title>トップ・ページ</title> </h:head> <h:body> <h:form> <h:link value="ユーザ登録画面へ" outcome="/user-manage/regist.xhtml" /><br/> <h:link value="ユーザ削除画面へ" outcome="/user-manage/remove.xhtml" /><br/> </h:form> </h:body> </html>

4.6 ユーザ登録・削除アプリケーションの実行 ユーザ登録・削除を行なうWebアプリケーションの実行準備が全て整いましたので、アプリケーションを実行して動作確認しましょう。 NetBean のプロジェクトを下記の手順で実行してください。「WebSocket-Mailer」プロジェクトが選択された状態でNetBeans のツールバーより「プロジェクト(WebSocket-Mailer)を実行(f6)」ボタンを押下してください。

図  64:プロジェクトの実行

図  63:登録・削除用 JSFページ一覧

Page 56: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  56  

もしくは、「WebSocket-Mailer」プロジェクトを選択し右クリックしてください。右クリックするとメニューが表示されますので、「実行」を選択してください。 プロジェクトを実行すると下記の画面がブラウザ上に表示されます。

図  65:プロジェクトの実行

図  66:プロジェクトの実行

Page 57: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  57  

ここで「ユーザ登録画面へ」のリンクを押下してください。押下すると下記の画面が表示されます。 ここで、各フィールドに下記を入力し「ユーザ登録」ボタンを押下してください。

表  1:登録するユーザ情報  

項目 入力値 ユーザ名 admin メールアドレス [email protected] パスワード admin 所属グループ admin

ボタンを押下すると下記の画面が表示されます。ここで()カッコ内に「ユー

図  67:ユーザ情報登録画面

図  68:ユーザ情報登録画面

Page 58: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  58  

ザ名」で入力した文字列が表示されている事を確認してください。 Web ページからユーザ登録した後、データベースに正しく登録されているか否か確認してください。NetBeans の「サービス」タブを選択し「USERTABLE」を選択し右クリックしてください。右クリックするとメニューが表示されます。ここで「データを表示...」を選択してください。

図  69:ユーザ情報登録完了画面

図  70:DBテーブル中のデータを表示

Page 59: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  59  

選択すると下記の画面が表示されます。ここで「PASSWORD」のカラムに入力されているデータが平文ではなくメッセージ・ダイジェスト「8c6976e5b...」になっていることを確認してください。 同様に、「GROUPTABLE」のデータを表示してください。「admin」ユーザに対して「admin」の GROUPID(ロール:役割)が割り当てられている事を確認してください。 「ユーザ登録画面」から同様にもう1名「user」のロールを持つユーザを追加してください。

表  2:登録するユーザ情報  

項目 入力値 ユーザ名 user メールアドレス [email protected] パスワード user 所属グループ user

以上でユーザの登録が正常に行なわれました。

図  71:DBテーブル中のデータを表示

図  72:DBテーブル中のデータを表示

Page 60: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  60  

5. アプリケーションに対するアクセス制限:認証・認可 次に本章では、アプリケーション・サーバ上で提供されているサービスに対して、データベースを利用して認証を行ない、認可設定によりアプリケーションの保護を行います。ここでご紹介する内容は、JavaServer Faces のアプリケーションに限らず、Servlet, JAX-RS、EJB など Java EE アプリケーション全体で適用可能です。Java EE 標準のセキュリティ実現方法4のため是非正しく理解してください。 今回作成するアプリケーションは下記のイメージです。 ログイン画面を作成し「管理者権限を持つユーザ」でログイン(認証)した場合は、管理者だけが実行可能な機能一覧が表示されるページを表示し(認可)、「ユーザ権限を持つユーザ」でログイン(認証)した場合は、ユーザが実行可能な機能一覧が表示されるページを表示します(認可)。

それではより詳細にアプリケーションに対する認証・認可を実現するための方法を説明します。 今回認証・認可を実現するために、JDBCレルムを使用します。レルムとはユーザ情報やグループ情報、そして関連するセキュリティ資格などの情報が保存された場所で、GlassFish ではレルムを実現する方式として下記が利用可能です。また下記の認証方式がサポートされています。

                                                                                                               4  ここでご紹介する内容は Java EE 6, Java EE 7 共通です。  

図  73:ログイン画面

Page 61: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  61  

GlassFish で利用可能なレルム方式

l LDAPレルム l クライアント認証・レルム l ファイル・レルム l Solaris レルム l JDBCレルム l PAMレルム l 独自作成のカスタム・レルム

GlassFish で利用可能な認証方式

l Basic 認証 l FORM認証 l クライアント証明書による認証 l ダイジェスト認証 l 相互認証

これらの中で今回は、JDBCレルムを使用した FORM認証を行ないます。 既に、「4.0 ユーザ・グループの登録・削除画面の作成」でデータベースのテーブル作成は完了しています。そこで本章ではアプリケーション・サーバ上の設定と、ログイン認証用のページを作成します。 実装手順は「図  74:JDBCレルムを利用した認証・認可の概念図」に示す順に行います。

図  74: JDBCレルムを利用した認証・認可の概念図

Page 62: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  62  

5.1 ログイン用の JSFページと JSF管理対象Beanの作成 まず、JSF でログインページと、ログインページのデータ管理を行なう管理対象Bean を作成します。 まず、ログイン認証前後に表示するページをまとめた新しいフォルダを作成します。「WebSocket-Mailer」プロジェクト配下の「Webページ」を選択し右クリックしてください。右クリックするとメニューが表示されますので、「新規」→「その他...」を選択してください。

図  75: JSFログインページの作成

図  76:新規フォルダの作成

Page 63: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  63  

選択すると下記のウィンドウが表示されます。ここで「カテゴリ(C):」より「その他」、「ファイル・タイプ(F):」より「フォルダ」を選択し「次 >」ボタンを押下してください。 ボタンを押下すると下記の画面が表示されます。ここで「フォルダ名(N):」に「login」と入力し、最後に「終了(F)」ボタンを押下してください。

図  77:新規フォルダの作成

図  78:新規フォルダの作成

Page 64: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  64  

次にログイン用の JSF ページを作成します。作成した「login」ディレクトリを選択し右クリックしてください。右クリックするとメニューが表示されますので、「新規」→「その他...」を選択してください。 選択すると下記のウィンドウが表示されます。ここで「カテゴリ(C):」より「JavaServer Faces」、「ファイル・タイプ(F):」より「JSF ページ」を選択し「次 >」ボタンを押下してください。

図  79:JSFログインページの作成

図  80: JSFログインページの作成

Page 65: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  65  

ボタンを押下すると下記の画面が表示されます。ここで「ファイル名:」に「index」と入力し、最後に「終了(F)」ボタンを押下してください。 ボタンを押下すると下記の Facelets が自動的に生成されます。自動生成されたコードを編集し下記のコードを記述してください。 <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:p="http://primefaces.org/ui" xmlns:f="http://xmlns.jcp.org/jsf/core"> <h:head> <title>ログイン画面</title> </h:head> <h:body> <h:form> <center> <p:panel header="ログイン画面" style="width: 320px"> <h:panelGrid columns="2" columnClasses="column" cellpadding="5"> <h:outputText value="ログイン名: " style="font-size:14px;"/> <h:inputText id="userName" value="#{indexPage.username}" style="font-size:14px;"/> <h:outputText value="パスワード: " style="font-size:14px;" /> <h:inputSecret onselect="true" id="password" value="#{indexPage.password}" style="font-size:14px;"/> </h:panelGrid> <h:commandButton value="ログイン" style="font-size:14px;" action="#{indexPage.login}"/> <h:messages id="messages" showDetail="true" errorStyle="color:RED;font-size:14px;" />

図  81: JSFログインページの作成

Page 66: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  66  

<h:commandLink value="ユーザ登録" action="/faces/user-manage/regist.xhtml" /> / <h:commandLink value="ユーザ削除" action="/faces/user-manage/remove.xhtml" /> </p:panel> </center> </h:form> </h:body> </html>

つづいて、この「JSF ページ」の処理を行なう「JSF 管理対象Bean」を作成します。「ソース・パッケージ」中から「jp.co.oracle.websocket.mailer.cdis」パッケージを選択し右クリックしてください。右クリックすると下記のメニューが表示されますので、「新規」→「その他...」を選択してください。

図  82:JSF管理対象 Beanの作成

Page 67: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  67  

選択すると下記のウィンドウが表示されます。ここで「カテゴリ(C):」に「JavaServer Faces」、「ファイル・タイプ(F):」に「JSF 管理対象 Bean」を選択し「次 >」ボタンを押下してください。 ボタンを押下すると下記の画面が表示されます。ここで「クラス名(N):」に「IndexPage」、「スコープ:」で「request」を選択し、最後に「終了(F)」ボタンを押下してください。

図  83:JSF管理対象 Beanの作成

図  84:JSF管理対象 Beanの作成

Page 68: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  68  

ボタンを押下すると「IndexPage」クラスが自動的に生成されます。自動生成されたコードに下記のコードを追加してください。 package jp.co.oracle.websocket.mailer.cdis; import java.util.logging.Level; import java.util.logging.Logger; import javax.inject.Named; import javax.enterprise.context.RequestScoped; /** * * @author Yoshio Terada */ @Named(value = "indexPage") @RequestScoped public class IndexPage { private String username; private String password; public IndexPage() { } /* ログインボタンが押下された際の処理 */ public String login() { String name = getUsername(); String pass = getPassword(); Logger.getLogger(IndexPage.class.getName()).log(Level.INFO, name +":"+ pass); return ""; } /** * @return the username */ public String getUsername() { return username; } /** * @param username the username to set */ public void setUsername(String username) { this.username = username; } /** * @return the password */ public String getPassword() { return password; } /** * @param password the password to set */ public void setPassword(String password) { this.password = password; } }

Page 69: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  69  

プログラムを修正した後、アプリケーションを実行してください。 NetBeans のメニューより「実行ボタン」を押下してください。   プロジェクトを実行すると、ブラウザが自動的に起動されます。ブラウザより下記のURLにアクセスしてください。 http://localhost:8080/WebSocket-Mailer/faces/login/index.xhtml アクセスすると下記の画面が表示されます。 ここで、「ログイン名:」に「admin」、「パスワード:」に「admin」と入力し「ログイン」ボタンを押下してください。すると NetBeans の「出力」ウィンドウの「GlassFish」のコンソール上に「情報:admin:admin」と表示される事を確認できます。 以上で、JSF のログインページから JSF の管理対象Bean に対して、データの送信ができる事を確認できました。

図  85:ログイン画面

図  86:GlassFishのコンソール・ログ出力

Page 70: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  70  

ここで、何らかのエラーが発生した時に表示するエラー・ページも作成しておきます。「図  79:JSFログインページの作成」と同様の手順で JSF のWebページ「error.xhtml」を「login」ディレクトリ配下に作成してください。 <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> <h:head> <title>エラー画面</title> </h:head> <h:body> <h:form> エラー画面 <h:commandButton value="戻る" action="#{indexPage.logout()}"/> </h:form> </h:body> </html>

続いてログインが成功した際に表示する画面も「図  79:JSFログインページの作成」と同様に「login」ディレクトリ配下に「home.xhtml」として作成してください。 <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> <h:head> <title>ログイン完了画面</title> </h:head> <h:body> <h:form> ログインユーザ名:<h:outputLabel value="#{request.userPrincipal.name}"/> でログインしました。<br/> <h:commandButton value="ログアウト" action="#{indexPage.logout()}"/> </h:form> </h:body> </html>

Page 71: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  71  

最後にトップ・ページからログインページへ画面遷移するためにリンクを追加してください。「Webページ」配下に存在する「index.html」を選択して下記のように修正してください。 <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> <h:head> <title>トップ・ページ</title> </h:head> <h:body> <h:form> <h:link value="ログイン画面へ" outcome="/login/index.xhtml"/><br/> <h:link value="ユーザ登録画面へ" outcome="/user-manage/regist.xhtml" /><br/> <h:link value="ユーザ削除画面へ" outcome="/user-manage/remove.xhtml" /><br/> </h:form> </h:body> </html>

Page 72: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  72  

5.2 GlassFish における JDBCレルムの設定と実装 クライアント・ブラウザのログイン画面から、ユーザ名、パスワードを入力し、サーバ上に送信できるようになりました。5そこで、このサーバで取得したユーザ名、パスワードとデータベースに存在するユーザ情報を元に認証を行ないます。アプリケーション・サーバからデータベースのユーザ情報、グループ情報を参照するために JDBCレルムを設定してください。JDBCレルムの設定は、asadmin コマンド6、もしくはGUI の管理コンソールから設定できます。 その前に、もう一度「4.0 ユーザ・グループの登録・削除画面の作成」で作成したデータベースのテーブルを確認します。 上記データベースの各テーブルを参照するように、JDBCレルムの設定を行います。

表  3:JDBCレルムの設定項目と設定値  

項目 項目の概要説明と設定値 名前 web.xml の設定で指定するレルム名

<login-config> <realm-name> レルム名 </realm-name> </login-config> 設定値:jdbc-realm

クラス名 GlassFish の JDBCRealm の実装クラス名

                                                                                                               5  この時点でユーザ名、パスワードは HTTP で送信されているためパケット盗聴などによりパスワード情報が漏洩します。HTTPS でデータを送信する方法は後ほど設定します。 6  asadmin コマンドは GlassFish をインストールしたディレクトリ配下の bin ディレクトリに存在します。例:/Applications/NetBeans/glassfish-4.0 にインストールした場合、/Applications/NetBeans/glassfish-4.0/bin に存在します。  

図  87:作成済みのデータベース・テーブル

Page 73: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  73  

設定値:com.sun.enterprise.security.ee.auth.realm. jdbc.JDBCRealm

JAAS コンテキスト JAAS (Java Authentication and Authorization Service)コンテキスト(このレルムに使用するログイン・モジュールの識別子)。 有効な値は jdbcRealm のみ 設定値:jdbcRealm

JNDI レルムで使用するデータベースのユーザ、グループ・テーブルを参照するための「JDBC リソース」に対する JNDI 名 設定値: jdbc/__default ※ 本来認証用の「JDBC 接続プール」、「JDBC リソース」を作成すべきだが、今回は検証のため JDBC のデフォルト・リソース「jdbc/__default」を使用

ユーザ表 認証対象のユーザ、パスワードが保存されているデータベースのテーブル名 設定値:APP.USERTABLE

ユーザ名列 ユーザ・テーブルのユーザ名のカラム名 設定値:USERNAME

パスワード列 ユーザ・テーブルのパスワードのカラム名 設定値:PASSWORD

グループ表 所属グループが保存されているデータベースのグループ名 設定値:APP.GROUPTABLE

グループ表ユーザ名列

グループ・テーブルのユーザ名のカラム名 設定値:USERNAME

グループ名列 グループ・テーブルのグループ名のカラム名 設定値:GROUPID

パスワード暗号化 アルゴリズム

データベースに格納されるパスワードを暗号化するためのアルゴリズム 設定値:AES

グループの割当て グループ名のカンマ区切りリスト 設定値:省略

データベース・ユーザー

JDBC のコネクション・プールを使用しない場合の DBへの接続ユーザ名 設定値:省略 今回 JDBC リソース(jdbc/__default)を利用するため省略

データベース・パスワード

JDBC のコネクション・プールを使用しない場合の DBへの接続パスワード 設定値:省略 今回 JDBC リソース(jdbc/__default)を利用するため省略

ダイジェスト・ アルゴリズム

パスワードのダイジェスト・アルゴリズムを指定

Page 74: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  74  

設定値:SHA-256 (デフォルト値) エンコーディング パスワードのエンコーディング(Hex, Base 64 を指定可能)

設定値:Hex

文字セット ダイジェスト・アルゴリズムの文字セット 設定値:UTF-8

asadmin コマンドから、上記の設定項目を元に JDBCレルムを作成する場合、下記のコマンドを実行してください。 $ ./asadmin create-auth-realm --classname com.sun.enterprise.security.ee.auth.realm.jdbc.JDBCRealm --property jaas-context=jdbcRealm: datasource-jndi=jdbc/__default: user-table=APP.USERTABLE: user-name-column=USERNAME: password-column=PASSWORD: group-table=APP.GROUPTABLE: group-table-user-name-column=USERNAME: group-name-column=GROUPID: digestrealm-password-enc-algorithm=AES: digest-algorithm=SHA-256: encoding=Hex: charset=UTF-8 jdbc-realm $ ./asadmin set server-config.security-service.activate-default-principal-to-role-mapping=true

GlassFish の管理コンソールから設定を行なう場合、ブラウザから下記GlassFish の管理コンソールのURLにアクセスしてください。 http://localhost:4848 [Windows, Linux 環境] Windows, Linux 環境で、GlassFish の asadmin コマンド、もしくはGUIの管理コンソールにアクセスする際に、管理者パスワードの入力を促された場合、デフォルトで設定されている管理者パスワードは、下記の方法で取得できます。まず、NetBeans の「サービス」タブより「サーバ」→「GlassFish Server 4.0」を選択してください。選択した後右クリックし「プロパティ」を選択してください。 ※ Mac OS/X の環境ではデフォルトで管理者パスワードの入力は求められません。

Page 75: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  75  

選択すると下記のウィンドウが表示されます。ここで「パスワード(W) :」と記載されている箇所を確認します。ウィンドウを表示した時点では「●●●●●」と表示されています。ここで「表示」ボタンを押下するとデフォルトのパスワードが表示されます。ここに記入されている内容をメモ帳などに控えて実行してください。

図  88:GlassFishのプロパティ

図  89:GlassFishのデフォルト・パスワード取得

Page 76: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  76  

GlassFish の管理コンソールに接続すると下記のような画面が表示されます。ここで、左ペインより「構成」→「server-config」→「セキュリティ」のツリーを展開し「レルム」を選択してください。選択すると右ペインに「レルム」の管理用ウィザードが表示されます。 ここで「新規...」ボタンを押下してください。新規ボタンを押下し「クラス名:」に「com.sun.enterprise.security.ee.auth.realm.jdbc.JDBCRealm」を選択すると下記の画面が表示されます。 ここで、「表   3:JDBC レルムの設定項目と設定値」の内容を記入して「OK」ボタンを押下してください。

図  90:GlassFish管理コンソールにおけるレルム設定

Page 77: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  77  

図  91:新規レルムの作成画面

Page 78: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  78  

新規 JDBCRealmを作成した後、デフォルトのロール・マッピングのプリンシパルを変更します。「図  91:新規レルムの作成画面」で「グループの割当て:」の設定項目を未入力にしましたが、下記のチェックを付けておく事で、アプリケーション・サーバが自動的にデータベースのグループ名をロール名として利用できるようになります。7 また、今回ご紹介する方法では、Java EE 6 の Servlet 3.0 で追加された、login, logout のメソッドも利用します。 以上で JDBCレルムを作成し、データベースに存在するユーザ・グループテーブルを認証目的で利用できるようになりました。

5.3 認証設定、認証コードの実装、動作確認 上記でアプリケーション・サーバから認証用データベースの参照ができるようになりました。そこで、ここではアプリケーション側で認証設定、実装を行ないます。 まず、Webアプリケーションで認証ができるように設定変更を行ないます。プロジェクト「WebSocket-Mailer」→「Webページ」→「WEB-INF」ディレクトリ配下に存在する「web.xml」をダブル・クリックしてください。ダブル・クリックすると NetBeans の右ペインに下記の画面が表示されます。そこで「セキュリティ」タブを押下してください。

                                                                                                               7  グループ名とロール名は必ずしも一致させる必要はありませんし、一つのロールに対して複数のグループを割り当てる事もできます。これらの設定を行なう場合は、XML の設定ファイルで行ないます。  

図  92:デフォルトのロール・マッピングのプリンシパルの設定

Page 79: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  79  

タブを押下すると下記の画面が表示されます。ここで「ログイン構成」を行ないます。今回、FORM 認証を行いますので「フォーム(F)」のラジオボタンにチェックします。次にログインページとして「login」ディレクトリ配下に存在する「index.xhtml」ファイルを使用しますので「フォームのログイン・ページ(L):」に「/faces/login/index.xhtml」を入力します。また「フォームのエラー・ページ(P):」に「/faces/login/error.xhtml」と入力してください。 また、認証方式には「表  3:JDBCレルムの設定項目と設定値」で設定したJDBCレルムを使用するため、「レルム名(R):」には「jdbc-realm」と入力してください。全て入力したのちweb.xml を保存してください。 保存すると、下記のコードが「web.xml」に記載されます。

図  93:web.xmlの編集

図  94:ログイン構成の設定

Page 80: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  80  

<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"> 前略・・・ <login-config> <auth-method>FORM</auth-method> <realm-name>jdbc-realm</realm-name> <form-login-config> <form-login-page>/faces/login/index.xhtml</form-login-page> <form-error-page>/faces/login/error.xhtml</form-error-page> </form-login-config> </login-config> 後略・・・ </web-app>

続いて「セキュリティ」の「セキュリティ・ロール」を設定します。今回データベースに登録したユーザが所属するグループ・テーブルは「admin」もしくは「user」の役割として登録しています。そこで「セキュリティ・ロール」に対して2つのロールを追加してください。「追加(A)...」ボタンを押下して下記2つのロールを追加してください。ロールを追加後「web.xml」ファイルを保存してください。 保存すると、下記のコードが「web.xml」に記載されます。 <web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"> 前略・・・ <security-role> <description>管理者用のロール</description> <role-name>admin</role-name> </security-role> <security-role> <description>一般ユーザ用のロール</description> <role-name>user</role-name> </security-role>

図  95:セキュリティ・ロールの設定

Page 81: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  81  

後略・・・ </web-app>

最後に「セキュリティ制約」を設定します。「セキュリティ制約の追加」ボタンを押下してください。 ボタンを押下すると下記の画面が表示されます。ここで新しいセキュリティ制約を追加します。「表示名(N):」に「Authrized Resource」(識別用の任意の文字)と入力してください。 続いて「webリソース・コレクション(W):」の「追加(A)...」ボタンを押下し「名前」、「URLパターン」、「HTTPメソッド」、「説明を」入力してください。ここでは、「/faces/login/*」ディレクトリ配下に存在する全Webページに対して制約を掛けます。またHTTPのメソッドとして「GET」もしくは「POST」を許可します。 次に「認証制約を有効にする(H)」のチェック・ボタンにチェックし、「編集(I)」ボタンを押下してください。ボタンを押下すると「図  95:セキュリティ・ロールの設定」で設定したロールが表示されますので「admin」、「user」のロールを「追加 >」のボタンを押下して追加し「OK」ボタンを押下してください。 最後に「/faces/login/*」ディレクトリ配下に存在するWebページに対してHTTPSで接続する設定を行ないます。「ユーザ・データ制約を有効にする(U)」にチェックし「トランスポート保証(T)」のコンボ・ボックスより「CONFIDENTIAL」を選択してください。設定が完了したのち「web.xml」ファイルを保存してください。

図  96:セキュリティ制約の追加

図  97:セキュリティ制約の追加

Page 82: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  82  

保存すると、下記のコードが「web.xml」に記載されます。 <web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"> 前略・・・ <security-constraint> <display-name>Authorized Resource</display-name> <web-resource-collection> <web-resource-name>protected-page</web-resource-name> <description>ログイン認証後に表示されるリソース</description> <url-pattern>/faces/login/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <description>認証完了したユーザが参照できるリソース</description> <role-name>admin</role-name> <role-name>user</role-name> </auth-constraint> <user-data-constraint> <description>このリソースに対しては HTTPS での接続が必須</description> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> 後略・・・ </web-app>

以上で、アプリケーションの設定は完了です。 それでは、ここでプロジェクトを実行して設定後のWebアプリケーションの振る舞いを確認してみましょう。NetBeans より「WebSocket-Mailer」プロジェクトを選択し実行してください。実行するとブラウザ上で下記の画面が表示されます。   図  98:プロジェクトの実行  

Page 83: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  83  

ここで、「ログイン画面へ」のリンクを押下してください。「図  97:セキュリティ制約の追加」の「トランスポート・保証」の設定で「CONFIDENTIAL」と設定した通り、自動的に 「https:localhost:8181/******」 へリダイレクトされる事を確認できます。 また、現在「/faces/login/」ディレクトリ配下には「index.xhtml」の他に「home.xhtml」のファイルが存在しますが、「/faces/login/*」ディレクトリ配下のコンテンツにアクセス制限が掛かっているかを確認します。ログインしていない状態でブラウザより下記の URL にアクセスしてください。 https://localhost:8181/WebSocket-Mailer/faces/login/home.xhtml 「/faces/login/」ディレクトリ配下のコンテンツに直接URLを指定してアクセスしても「ログイン画面」が表示されアクセス制限が掛かっていることを確認できます。

図  100:コンテンツに対するアクセス制限

図  99:CONFIDENTIAL設定による HTTPSへリダイレクト

Page 84: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  84  

それでは実際に、認証を行なうためのコードを実装します。「IndexPage」 クラスの「login」メソッドに対して下記のコードを実装してください。ここでは「Java EE 6」の「Servlet 3.0」に含まれる「HttpServletRequest」で新たに追加されたメソッド「login」メソッドを使用してログイン処理を行なっています。 ログインが成功した際、画面遷移先の return 値として「home.xhtml」と指定していますが、URLだけではなく下記のようにリダイレクトも指定しています。「home.xhtml?faces-redirect=true」このリダイレクトの設定は非常に重要です。JSF ではリダイレクト指定を行なう事でURLが「/faces/login/home.xhtml」へ切り代わり(リダイレクトされ)ます。 リダイレクトを行なわない場合、URLは「/faces/login/index.xhtml」のまま、「home.xhtml」の内容が表示されます。 「図  97:セキュリティ制約の追加」で設定したように「/faces/login/*」ディレクトリ配下に存在するWebページは、認証完了後「admin, user」の権限を持つユーザだけが表示できます。しかしURLが変わらない場合(つまりログインページのと同じURLの場合)、仮にDBとの認証に成功したユーザで、「/faces/login/*」ディレクトリ配下にアクセス権限を持たないユーザが存在した場合、URLが切り替わらないため「home.xhtml」の内容が表示されてしまいます。リダイレクトを行なうことで正しく認可機能が働きますので必ずリダイレクトを行なってください。 リダイレクト設定により、所属するグループのロールによって認可されていない(ここでは admin, user 以外のグループのメンバー)場合、サーバから「HTTP Status 403 - Forbidden」が返されるようになります。 @Named(value = "indexPage") @RequestScoped public class IndexPage { public String login() { FacesContext context = FacesContext.getCurrentInstance(); ExternalContext externalContext = context.getExternalContext(); HttpServletRequest request = (HttpServletRequest) externalContext.getRequest(); try { request.login(getUsername(), getPassword()); } catch (ServletException ex) { context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "ログインに失敗しました。", "ユーザ名、パスワードを正しく入力してください。")); return ""; } return "home.xhtml?faces-redirect=true"; } }

つづいて、「IndexPage」クラスに対してログアウト用の「logout」メソッドも実装してください。ここではセッション情報を持つ場合破棄し、Servlet 3.0 の「HttpServletRequest」に新しく追加された「logout」メソッドを

Page 85: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  85  

使用してログアウトを行なっています。処理が終わったのち「index.xhtml」へリダイレクトしています。 @Named(value = "indexPage") @RequestScoped public class IndexPage { public String logout() { ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext(); externalContext.invalidateSession(); HttpServletRequest request = (HttpServletRequest) externalContext.getRequest(); try { request.logout(); } catch (ServletException ex) { Logger.getLogger(IndexPage.class.getName()). log(Level.SEVERE, “ログアウト失敗”, ex); } return "index.xhtml?faces-redirect=true "; } }

以上で、認証設定と認証コードの実装は完了です。 それでは実際にDBに登録されているユーザで認証、認可ができているか確認します。 今データベースには「admin」、「user」というユーザが登録されています。 ※登録していない場合、再度「図  67:ユーザ情報登録画面」を参照し登録してください。 また、「admin」ユーザに対して所属グループは「admin」、「user」ユーザに対して所属グループは「user」が設定されていることを確認してください。

図  101:データベースに登録されている情報確認

図  102:データベースに登録されている情報確認

Page 86: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  86  

これらのユーザ名、パスワードを利用してログインができるか否かを確認します。プロジェクトを実行して「ログイン画面」を表示し、ユーザ名「admin」、パスワード「admin」を入力したのち、「ログイン」ボタンを押下してください。 「ログイン」ボタンを押下すると画面が遷移し、ブラウザ上で下記のメッセージが表示されます。この際、画面遷移先のURLが下記のように「home.xhtml」へ正しくリダイレクトされている事も確認してください。 https://localhost:8181/WebSocket-Mailer/faces/login/home.xhtml ここで、「ログアウト」ボタンを押下してください。再度「ログイン画面」が表示されますので、続いて「user」でログインします。

図  103:ログイン画面

図  104:admin  ユーザのログイン完了画面

図  105:ログイン画面

Page 87: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  87  

「ログイン」ボタンを押下すると画面が遷移し、ブラウザ上で下記のメッセージが表示されます。 上記で、「admin」、「user」でログインできるようになりましたので、データベースに対して新たなユーザ「invaliduser」を追加し、コンテンツに対してアクセスが認められていない所属グループ「invalidgroup」を設定します。ユーザ情報登録画面に接続し、下記の内容を登録してください。パスワードはここでは「invaliduser」としています。 登録が完了すると下記がブラウザに表示されます。

図  106:user  ユーザのログイン完了画面

図  107:コンテンツにアクセスが禁じられているユーザの作成

図  108:コンテンツにアクセスが禁じられているユーザの作成完了

Page 88: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  88  

ユーザを登録後、再度「ログイン画面」に接続して作成した「invaliduser」でログインを行なってください。 「ログイン」ボタンを押下するとブラウザ上に下記が表示されます。 以上で、「/faces/login/*」ディレクトリ配下に存在するコンテンツが「admin」もしくは「user」の役割を持つユーザしかアクセスできない事が確認できました。上記でデータベースによる認証・認可を確認できました。

5.4 細かいカスタマイズ 上記により、認証・認可を実現するための設定、実装、検証は終わりましたが、現時点の実装ではいくつか仮題が残っています。たとえば、ログインが完了した後でも、「ログイン画面」を表示可能です。またログイン後、自動的に「home.xhtml」へ画面遷移した後、ブラウザの戻るボタンで「ログイン画面」へ戻ることも可能です。さらには、アクセス権限がないユーザがログインした際に「図  110:HTTP  Status  403  -­‐  Forbidden  の表示」が表示されることも画面設計上好ましくありません。そこでそれらの仮題に対して対応を行ないます。

図  109:invalid  ユーザでログイン

図  110:HTTP  Status  403  -­‐  Forbidden  の表示

Page 89: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  89  

ログイン完了後、ログイン画面を非表示 現在、たとえば「admin」でログインした後に再度「ログイン画面」のURLにアクセスすると再度「ログイン画面」が表示されます。 ログインが完了した後、ブラウザのアドレス・バーに直接下記のURLを入力しアクセスしてください。下記の画面が表示されます。 一度、ログインしたユーザに、ログアウトするまで上記の画面を二度と表示させないようにするために、JSF の Facelets と CDI をそれぞれ修正します。8

                                                                                                               8  以降では admin, user のユーザを利用してください。本Web アプリケーションではinvaliduer のためのログアウト・コードを実装していません。仮に Invaliduesr でログインしてしまった場合は、一度ブラウザを閉じて再度立ち上げ直してログインしなおしてください。

図  111:admin  によるログイン

図  112:ログイン画面の再表示

Page 90: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  90  

まず、CDI 側の編集を行ないます。IndexPage クラスに対して新たに「onPageLoad()」メソッドを作成してください。このメソッドは、メソッドが呼び出された際、「HttpServletRequest」の「getUserPrincipal()」メソッドを呼び出し「Principal」オブジェクトを取得しています。一旦ログインが完了したユーザはこの「Principal」オブジェクトに値が代入されています。そこで、このオブジェクトが null でない場合は、ログイン済みのユーザと判定できます。仮にログイン済みのユーザの場合は、ログイン後に表示するページ「/home.xhtml」へ強制的にリダイレクトします。 @Named(value = "indexPage") @RequestScoped public class IndexPage { /* 既にログイン済みだった場合、ログイン後のページ(home.xhtml)へリダイレクトし login.xhtml を非表示 */ public void onPageLoad() throws ServletException { FacesContext context = FacesContext.getCurrentInstance(); ExternalContext externalContext = context.getExternalContext(); HttpServletRequest request = (HttpServletRequest) externalContext.getRequest(); Principal principal = request.getUserPrincipal(); if(principal != null){ try { StringBuilder redirectURL = new StringBuilder(request.getContextPath()); redirectURL.append("/faces/login/home.xhtml"); FacesContext.getCurrentInstance().getExternalContext(). redirect(redirectURL.toString()); } catch (IOException ex) { request.logout(); } } } }

次に「index.xhtml」のWebページ側を修正します。「index.xhtml」ファイルを開き、下記「f:event」タグの1行を追加してください。 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:p="http://primefaces.org/ui" xmlns:f="http://xmlns.jcp.org/jsf/core"> <h:head> <title>JSF-WebSocket WebMail ログイン画面</title> <f:event type="preRenderView" listener="#{indexPage.onPageLoad}"/> </h:head> <h:body> <h:form>

これにより「index.xhtml」へアクセスされた際、ビューがレンダリングを行なう前に、「IndexPage#onPageLoad()」メソッドが呼び出され、既にロ

Page 91: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  91  

グイン済みの場合は、強制的に「home.xhtml」へリダイレクトするようになります。 上記を修正した後、動作確認をしてください。「admin」でログイン後、ブラウザのアドレス・バーに対して下記の URL を入力して直接「index.xhtml」へアクセスしてください。 アドレス・バーから直接入力した場合、「home.xhtml」へリダイレクトされることを確認できます。 以上で、ログイン後は「ログイン画面」が表示されなくなります。

図  113:ログイン完了後、アドレス・バーから index.xhtmlに強制アクセス

Page 92: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  92  

ブラウザの「戻る」ボタンによるログイン画面の非表示 上記の対応で、直接「index.xhtml」へアクセスされた場合はリダイレクトされるようになりました。しかし、ログイン後にブラウザの「戻る」ボタンを押下された場合は「ログイン画面」が表示されてしまいます。そこで「戻る」ボタンに対応したいと思います。これは一般的な JavaScript のコードを追加し実現します。「index.xhtml」ファイルに対して<script></script>タグ部分を追記してください。 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:p="http://primefaces.org/ui" xmlns:f="http://xmlns.jcp.org/jsf/core"> <h:head> <title>JSF-WebSocket WebMail ログイン画面</title> <f:event type="preRenderView" listener="#{indexPage.onPageLoad}"/> </h:head> <script> window.onunload = function() { }; history.forward(); </script> <h:body> <h:form> <center>

上記を記述した後、再度実行すると一度ログインした後、二度と「ログイン画面」が表示される事はなくなります。

Page 93: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  93  

HTTP Status 403 発生時に表示するエラー・ページ さて、現在「invaliduer」などアクセス権限がないユーザがログインした際に「図  110:HTTP  Status  403  -­‐  Forbidden  の表示」で示したようにアプリケーション・サーバが用意するエラー・ページが表示されます。そこで、発生する例外やHTTPのステータス・コードに応じて特定のエラー・ページを表示させるように設定変更を行ないます。 まず、「Webページ」ディレクトリ配下にエラー・ページ用のフォルダ「errors」を作成してください。 次に、「errors」ディレクトリ配下にファイル名「error-403.xhtml」のJSF ページを作成してください。

図  114:エラー・ページ用のフォルダを作成

図  115:error-­‐403.xhtmlファイルの作成

Page 94: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  94  

「error-403.xhtml」には、下記のコードを記載します。 <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> <h:head> <title>アクセス禁止</title> </h:head> <h:body> <h:form> 指定のコンテンツに対してアクセスする権限がありません。<br/> <h:link value="Top ページ" outcome="/index.xhtml"/> </h:form> </h:body> </html>

エラー・ページを作成した後、「web.xml」を編集してHTTPのステータス・コード:403が帰ってきた際に「error-403.xhtml」を表示するように設定します。「web.xml」ファイルをダブル・クリックして選択し「ページ」タブを押下してください。タブを押下すると下記の画面が表示されます。ここで「エラー・ページ」より「追加(A)...」ボタンを押下してください。

 図  116:web.xmlファイルの編集

Page 95: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  95  

ボタンを押下すると下記「エラー・ページの追加」ウィンドウが表示されます。ここで「参照...」ボタンを押下すると「ファイルを参照」ウィンドウが表示されますので、作成した「error-403.xhtml」ファイルを選択し、「ファイルを選択」ボタンを押下してください。 ファイルを選択すると下記画面が表示されます。ここで「OK」ボタンを押下してください。 ボタンを押下すると下記の画面が表示されます。表示後「web.xml」ファイルを保存してください。

図  117:エラー・ページの追加

図  118:エラー・ページの追加

図  119:エラー・ページの追加  

Page 96: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  96  

ファイルを保存すると「web.xml」に下記が追加されます。 <web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"> 前略… <error-page> <error-code>403</error-code> <location>/errors/error-403.xhtml</location> </error-page> 後略… </web-app>

ここで、静的なHTMLファイルであれば上記設定で問題ありませんが、エラー・ページも JSF のページとして実装したい場合、上記では動作しません。そこで、エラー・ページも JSF のページとして実装したい場合、下記のように「/faces」を先頭に追加して保存してください。 <error-page> <error-code>403</error-code> <location>/faces/errors/error-403.xhtml</location> </error-page>

最後に実行して、新たに作成したエラー・ページが表示されるか否か確認してください。「ログイン画面」を表示し「invaliduser」でログインしてください。 ボタンを押下すると下記のエラー画面が表示されます。9                                                                                                                9  今回は invaliduserのログアウト・メソッドを実装していません。そこで他のユーザ・アカウントで再度ログインできるようにするためには、Invaliduser用のログアウト処理を別途実装するか、もしくはブラウザを再起動してください。  

図  120:invaliduser  でログイン

図  121:アクセス禁止エラー・ページの表示

Page 97: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  97  

今回は、HTTP ステータス・コード 403 に対するエラー・ページを作成しましたが、同様の手順で他のステータス・コードに対するエラー・ページを用意できる他、特定の例外(Exception)が発生した時に表示するエラー・ページを設定する事も可能です。エラー・ページをカスタマイズしたい場合は、ここで行なってください。

5.5 処理単位でのアクセス制限方法 ここまでは、接続URLの単位でアクセス制限をかけてきました。これを利用すると特定の役割を持つ人しかアクセスができないページに誘導する事もできます。 しかし、レルムを使用すると、さらに機能単位でアクセス制限、実行権限を指定する事もできます。例えば、今「/faces/login/*」ディレクトリ配下のコンテンツは、「admin」もしくは「user」の役割を持つユーザであれば、誰でも「home.xhtml」に対して接続ができました。しかし、この「home.xhtml」のページ内で共通に表示されるコンテンツの他に、「admin」だけが表示できるコンテンツ、もしくは「user」だけが表示できるコンテンツといったように役割に応じて画面表示を切り変えたい場合もあります。このような場合、プログラム側でアクセス制限を掛けることも可能です。 ここでは、「home.xhtml」を利用して「admin」だけが表示されるコンテンツ、もしくは「user」だけが表示されるコンテンツの作成を行ないたいと思います。 まず、「home.xhtml」に対応する「JSF 管理対象Bean」を「HomePage.java」として作成してください。

図  122:home.xhtml用の JSF管理対象 Beanの作成

Page 98: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  98  

「JSF 管理対象Bean」を作成するとコードが自動的に生成されます。ここで、下記のようにログインしたユーザが、引数で指定したロールをもつユーザか否かを検証するメソッド「isUserInRole()」を実装してください。 package jp.co.oracle.websocket.mailer.cdis; import javax.inject.Named; import javax.enterprise.context.RequestScoped; import javax.faces.context.FacesContext; /** * * @author Yoshio Terada */ @Named(value = "homePage") @RequestScoped public class HomePage { /** * Creates a new instance of HomePage */ public HomePage() { } /* ログインしたユーザが、引数で指定した役割(ロール)を持つ ユーザか否かを検証 */ public boolean isUserInRole(String role){ return FacesContext.getCurrentInstance(). getExternalContext().isUserInRole(role); } }

次に、「home.xhtml」に下記 <h:outputLabel> を追加してください。ここで、JSF タグ内の rendered 属性の true, false で描画内容を切り替えています。例えば「admin」権限を持つユーザがログインした場合は「管理者の表示文字列」が表示され、「user」権限を持つユーザがログインした場合は、「ユーザの表示文字列」が表示されます。 <h:form> ログインユーザ名:<h:outputLabel value="#{request.userPrincipal.name}"/>でログインしました。<br/> <h:outputLabel value="管理者の表示文字列" rendered="#{homePage.isUserInRole('admin')}"/> <h:outputLabel value="ユーザの表示文字列" rendered="#{homePage.isUserInRole('user')}"/> <br/> <h:commandButton value="ログアウト" action="#{indexPage.logout()}"/> </h:form>

Page 99: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  99  

「home.xhtml」を保存したのち、プロジェクトを実行してください。実行した後、「admin」、「user」それぞれでログインした際、下記のように画面が表示されます。 また、EJB を使った場合は役割に応じてメソッド単位で実行権限を指定する事も可能です。EJBのメソッド単位での実行権限の検証を行なうために新たに EJBを作成してください。 EJB のパッケージを選択し右クリックしてください。右クリックするとメニューが表示されますので「新規」→「その他」を選択してください。

図  123:役割に応じた画面表示

図  124:新規 EJBの作成

Page 100: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  100  

選択すると下記のウィンドウが表示されます。ここで「カテゴリ(C):」より「エンタープライズ JavaBeans」、「ファイル・タイプ(F):」より「セッションBean」を選択し「次 >」ボタンを押下してください。 ボタンを押下すると下記の画面が表示されます。ここで「EJB名(N):」に「RoleCheckLogic」と入力し「終了(F)」ボタンを押下してください。

図  125:新規 EJBの作成

Page 101: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  101  

ボタンを押下するとステートレス・セッションBean が自動生成されます。ここで、@RolesAllowed のアノテーションを付加した2つのメソッドを追加してください。このアノテーションを付加したメソッドは、該当の役割を持つユーザしか実行できなくなります。 仮に権限を持たないユーザが実行した場合、例外が発生するようになります。 package jp.co.oracle.websocket.mailer.ejbs; import javax.annotation.security.RolesAllowed; import javax.ejb.Stateless; /** * * @author Yoshio Terada */ @Stateless public class RoleCheckLogic { @RolesAllowed("admin") public String executableByAdmin() { return "管理者による実行が可能なロジック"; } @RolesAllowed("user") public String executableByUser() { return "ユーザによる実行が可能なロジック"; } }

実際に実行権限の無いユーザが上記メソッドを呼び出した場合、下記のように javax.ejb.AccessLocalException が送出されます。 警告: StandardWrapperValve[Faces Servlet]: Servlet.service() for servlet Faces Servlet threw exception javax.ejb.AccessLocalException: クライアントはこの起動を承認されていません at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1895) at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:210) at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:88) at com.sun.proxy.$Proxy727.executableByAdmin(Unknown Source) at jp.co.oracle.websocket.mailer.ejbs.__EJB31_Generated__RoleCheckLogic__Intf____Bean__.executableByAdmin(Unknown Source) at jp.co.oracle.websocket.mailer.cdis.HomePage.getRoleChekerString(HomePage.java:31)

Page 102: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  102  

次に、上記の EJB のビジネス・ロジックを呼び出すために、CDI にメソッドを追加します。10「HomePage」クラスに対して下記のコードを追加してください。下記では作成した EJBをインジェクトし、「admin」権限を持つ場合、executableByAdmin()メソッドを実行し、「user」権限を持つ場合、executableByUser()メソッドを実行しています。 @Named(value = "homePage") @RequestScoped public class HomePage { 前略… @EJB RoleCheckLogic roleCheckLogic; private String roleChekerString; public String getRoleChekerString() { if (isUserInRole("admin")) { String adminRoleString = roleCheckLogic.executableByAdmin(); roleChekerString = adminRoleString; } if (isUserInRole("user")) { String userRoleString = roleCheckLogic.executableByUser(); roleChekerString = userRoleString; } return roleChekerString; } 後略… }

最後に roleChekerStringの文字列を画面に表示するために「home.xhtml」ファイルを編集します。<h:outputLabel>の2行を追加してください。 前略… <h:body> <h:form> ログインユーザ名:<h:outputLabel value="#{request.userPrincipal.name}"/> でログインしました。<br/> <h:outputLabel value="EJB 呼び出し結果:" /> <h:outputLabel value="#{  homePage.roleChekerString}" /><br/> 後略…

                                                                                                               10  @RolesAllowedアノテーションは Servlet,  JAX-­‐RS等でも利用できますが、CDIでは利用できません。CDIも宣言的にアクセス制限を掛けたい場合、独自の Interceptorを実装する方法などがあります。参考:http://www.mirkosertic.de/doku.php/architecturedesign/weldsewithee  

Page 103: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  103  

修正後、プロジェクトを実行してください。下記のように権限に応じた実行結果が得られます。

このようにして、Java EE のセキュリティ機構を使用すると、プログラム側でもより安全にアクセスの制限を掛ける事ができるようになります。@RolesAllowed のアノテーション以外に、Java EE では @DeclareRoles, @PermitAll, @DenyAll, @RunAs 等のアノテーションを付加してアクセス制限を掛けることができるほか、ロールを設定する事ができますので、これらを利用してよりセキュアな Java EE のアプリケーションを構築してください。 プログラム上で実装するセキュリティのより詳しい情報は下記のURLなどをご参照ください。 The Java EE 6 Tutorial Securing Enterprise Beans http://docs.oracle.com/javaee/6/tutorial/doc/bnbyl.html sli

図  126:宣言的なアクセス制限

Page 104: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  104  

Page 105: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  105  

Appendix.1

pom.xml  の設定内容   下記に、ハンズオン・ラボで作成するプロジェクトの pom.xml の設定内容を記載します。 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>jp.co.oracle</groupId> <artifactId>WebSocket-Mailer</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>WebSocket-Mailer</name> <repositories> <repository> <id>prime-repo</id> <name>PrimeFaces Maven Repository</name> <url>http://repository.primefaces.org</url> <layout>default</layout> </repository> </repositories> <properties> <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.primefaces</groupId> <artifactId>primefaces</artifactId> <version>4.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source>

Page 106: Java EE  Detail of JDBC-Realm

Java  EE  7  Hands-­‐on  Lab  using  GlassFish  4  

  106  

<target>1.7</target> <compilerArguments> <endorseddirs>${endorsed.dir}</endorseddirs> </compilerArguments> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.6</version> <executions> <execution> <phase>validate</phase> <goals> <goal>copy</goal> </goals> <configuration> <outputDirectory>${endorsed.dir}</outputDirectory> <silent>true</silent> <artifactItems> <artifactItem> <groupId>javax</groupId> <artifactId>javaee-endorsed-api</artifactId> <version>7.0</version> <type>jar</type> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>

以上