GlassFish で BASIC 認証を利用する (Servlet 編) (ja)

GlassFishの持つ認証機能については、yumix さんに寄稿して頂いた、
で概要は把握できるかと思います。今回から数回にわたって、サンプルプログラムを用いて具体的な実装方法を見てゆこうと思います。

まずは、サンプルプログラムの仕様を以下に示します。

リソース メソッド 入出力内容
/command GET 入力パラメータなし。文字列「Wait!」を返す。
POST 入力パラメータvalueを受け取り、文字列「Command %s is accepted!」を返す。valueがnullの場合は"Waiting"が入力されたものとみなす。
/operation GET 入力パラメータなし。文字列「Operation Desert Storm」を返す。

リソースに対しては次の認可を与えます。

リソース メソッド 認可(許可ロール)
/command GET sergeant, lieutenant
POST leader
/operation GET lieutenant

ユーザーは以下の3種類を用意しました。

ユーザー 認可(許可ロール)
vincent sergeant
haruka sergeant, leader
arthur lieutenant, leader

以上の要求をまとめると、次のようなアクセス権限となります。

ユーザー 認可(許可ロール)
vincent /commandに対するGETのみ許可
haruka /commandに対するGET/POSTを許可
arthur /commandに対するGET/POST、および/operationに対するGETを許可

この要求をもとに、今回はアプリケーションをサーブレットで実装してみようと思います。今回はServlet 3.0のアノテーションによるアクセス制御を使用しますので、web.xmlは作成しません。

CommandServlet.java
package sample.auth.file;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.HttpMethodConstraint;
import javax.servlet.annotation.ServletSecurity;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@ServletSecurity(httpMethodConstraints = { 
  @HttpMethodConstraint(value = "GET", rolesAllowed = { "sergeant", "lieutenant" }), 
  @HttpMethodConstraint(value = "POST", rolesAllowed = "leader") })
@WebServlet("/command")
public class CommandServlet extends HttpServlet {

  private static final long serialVersionUID = 1L;

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    PrintWriter out = resp.getWriter();
    out.print("Wait!");
    out.flush();
    out.close();
  }

  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String value = req.getParameter("value");
    PrintWriter out = resp.getWriter();
    out.printf("Command %s is accepted.", value == null ? "Waiting" : value);
    out.flush();
    out.close();
  }
}

OperationServlet.java
package sample.auth.file;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.HttpConstraint;
import javax.servlet.annotation.ServletSecurity;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@ServletSecurity(@HttpConstraint(rolesAllowed = "lieutenant"))
@WebServlet("/operation")
public class OperationServlet extends HttpServlet {

  private static final long serialVersionUID = 1L;

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    PrintWriter out = resp.getWriter();
    out.print("Operation Desert Storm!");
    out.flush();
    out.close();
  }
}

index.html (ドライバーモジュール) ※別途HTTPクライアントが用意できるなら不要です
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Auth Sample</title>
</head>
<body>
<h1>Auth Sample</h1>
<form id="form1" method="get" action="command">
<p>
  <input type="submit" name="send" value="show command"/>
  (for all)
</p>
</form>

<form id="form2" method="post" action="command">
<p>
  <input type="text" name="value" value=""/>
  <input type="submit" name="send" value="send command"/>
  (for leader)
</p>
</form>

<form id="form3" method="get" action="operation">
<p>
  <input type="submit" name="send" value="show operation"/>
  (for lieutenant)
</p>
</form>
</body>
</html>

sun-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Servlet 2.5//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_2_5-0.dtd">
<sun-web-app error-url="">
  <context-root>/authSample1</context-root>
  <security-role-mapping>
  	<role-name>leader</role-name>
  	<group-name>leader</group-name>
  </security-role-mapping>
  <security-role-mapping>
  	<role-name>lieutenant</role-name>
  	<group-name>lieutenant</group-name>
  </security-role-mapping>
  <security-role-mapping>
  	<role-name>sergeant</role-name>
  	<group-name>sergeant</group-name>
  </security-role-mapping>
  <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>
</sun-web-app>

sun-web.xmlはGlassFish 3.1以降、glassfish-web.xmlに置き換えられましたが、後方互換性のため当面使用できます(EclipseのGlassFish Toolsで自動生成されるのがsun-web.xmlで、NetBeansで自動生成されるのがglassfish-web.xmlです)。なお、sun-web.xmlとglassfish-web.xmlはXML Schema宣言とルート要素が異なることを除いてほぼ同一です。

他のファイルについてはお使いのIDEに合わせるなり、Mavenプロジェクトに合わせるなり、自由にしてください。アプリケーションが完成したらGlassFishにデプロイし、それぞれのリソースにアクセスしてみてください(例えばユーザーvincentで/operationにGETアクセスしようとしてHTTP 403が返る、など)。