認証処理の実装
従来のサーブレットには4つの認証方式がありました。基本認証、ダイジェスト認証、フォーム認証(FORM認証)、クライアント認証の4つです。中でもフォーム認証は、認証画面をJSPでカスタマイズすることができましたので使われる機会が多く、しかしFORM認証画面では、以下のタグを使うことが必須となっていました。
<form action="j_security_check">
これは、認証用のURLが固定され、またFORM認証の処理そのものをカスタマイズすることが難しいという問題がありました。従来のサーブレットでは、これらログイン機能に関する変更を行うことができず、Javaコンテナによっては拡張された実装を使う必要がありました。アプリケーションによっては、画面で入力することなく特定のユーザで代理認証を行う機能も必要とされますが、従来のサーブレットでは行うことができないため、独自拡張してJavaコンテナとは別の認証の仕組みを用意する手間がありました。サーブレット3.0ではこの<form action="j_security_check">の代替機能となる認証方式をサーブレットで実装できるようにしていおり、それぞれサーブレットのメソッド内で呼び出すことができます。この機能が提供されることにより、大掛かりな独自拡張することなく認証をカスタマイズすることができます。
認証情報の参照先については、従来通りJavaコンテナで設定されたものを使用します。管理方法は、アプリケーション内のxmlでユーザ情報を持つもの、データベースを利用して保存する方式、LDAPと呼ばれるディレクトリサービスでユーザ情報を階層的に格納しているサーバに問い合わせをする方法などが提供されています。
認証情報の参照設定についてはJavaコンテナでの設定となりますので、例えばTomcatで設定する場合には、以下のサイトを参考にして構築しましょう。
では話をサーブレット3.0での実装に移します。
(1)ログイン:HttpServletRequest.login("ユーザ名" , "パスワード")
引数に指定したユーザ名、パスワードを使って認証を行います。
もし認証に失敗した場合には例外(ServletException)がスローされますので、もしエラーページやエラーメッセージを独自で出したい場合には例外をキャッチする必要があります。
認証に成功した場合、このメソッドは特に何も返しませんので、認証しているかどうかは、HttpServletRequestのgetRemoteUser()が、getUserPrincipal()メソッドで情報が取得できるかどうかを確認することで判断します。
// サーブレット定義アノテーション @WebServlet(name="LoginServlet", urlPatterns={"/LoginServlet"}) public class AuthServlet extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // リクエストされたユーザ名を取得 String username = request.getParameter("username"); // リクエストされたパスワードを取得 String password = request.getParameter("password"); // 認証を行う。もし認証が失敗していればServletExceptionがスローされる request.login(username, password); // 認証済の場合、remoteUserにユーザ名が入る String remoteUser = request.getRemoteUser(); …(中略)… } }
このログインサーブレットの定義は、@WebServletで定義します。nameでサーブレットの名称を定義、urlPatternsでリクエストするURLを定義します。この例では /LoginServlet になっています。
(2)ログアウト:HttpServletRequest.logout()
ログアウト処理をJavaコンテナに対して行います。従来はセッションの破棄などを明示的に行いましたが、ログアウト処理を独立させて行うことによって、サーバ内で消去したくない情報まで消されてしまうことがなくなります。コードはログイン同様非常に簡潔です。
// サーブレット定義アノテーション @WebServlet(name="LogoutServlet", urlPatterns={"/LogoutServlet"}) public class LogoutServlet extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // ログアウト処理 request.logout(); …(中略)… } }
(3)コンテナ認証:HttpServletRequest.authenticate(HttpServletResponse)
認証結果をbooleanで受け取ります。この認証処理は、従来のBASIC認証やFORM認証を使って認証する場合に用います。ブラウザから入力されたログイン情報を使って、任意の位置でログイン処理を実行させることになります。これによって、認証前や認証後に処理を追加しやすくなりました。
@WebServlet(name="AuthServlet", urlPatterns={"/AuthServlet"}) public class AuthServlet extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 認証前の処理 …(省略)… // コンテナ認証を行う。結果をbooleanで取得。 boolean isAuth = request.authenticate(response); // 認証後の処理 …(省略)… } }
なお、これら認証設定の内容は、web.xmlでは<security-constraints>、アノテーションであれば@ServletSecurityで一括設定することもできます。
JARファイルからの静的コンテンツ読み込み
WEB-INF/lib/resource.jarなど、従来どおりのjarファイルと同じように配置したファイルの中から、HTMLやJSPを参照できるようになりました。以下のルールを守ることで利用可能です。
- jarファイル内にあるMETA-INF/resources/ 以下に静的ファイルやJSPを配置する
この機能の恩恵は大きいもので、jarにパッケージしたライブラリの中には、JSPの描画をサポートするテンプレートエンジンと呼ばれるものが存在し、現在はよく使われています。これらテンプレートエンジンの中身にはHTMLやCSSなど、本来Javaのクラスで構成されるjarファイルには含まれないものが必要とされますが、JavaコンテナはWEB-INF/libにjarを配置する規則がありましたので、これらの静的ファイルがあったとしても認識しませんでした。