この章では、アプリケーションでのカスタム認可の開発および構成方法について次の各項で説明します。
この情報を使用する前に、使用されているコンテキストに精通しておくことを強くお薦めします。詳細は、第14.3.3項「プログラム的な認可」を参照してください。
この項では、JavaEEとJAASのモデルで使用可能な認可を比較検討しています。
JavaEE認可モデルでは、EJBメソッドおよびURLで参照されるWebリソースへのアクセスが、ロール・メンバーシップを使用して制御されます。JAAS認可モデルでは、(ロール・メンバーシップではなく)パーミッションを使用して、アクセスの決定が制御されます。
JavaEEモデルでは、次のいずれかの方法で認可が実装されます。
宣言的。この方法では、認可ポリシーがデプロイメント・ディスクリプタで指定されます。コンテナはデプロイメント・ディスクリプタから認可ポリシーを読み取って実施します。認可を実施するための特別なアプリケーション・コードは必要ありません。
プログラム的。この方法では、認可ポリシーがアプリケーション・コードで指定されます。コードによって、サブジェクトが特定のコード・セクションの実行に適したパーミッションを持っているかどうかが確認されます。サブジェクトに適切なパーミッションが与えられていない場合、コードは例外をスローします。
表18-1は、各方法の長所と短所を示しています。
Java 2認可は、ロールではなくパーミッションに基づき、アクセス制御の決定は、SecurityManagerまたはAccessControllerへのコールによって評価されます。このモデルをJAASモデルと組み合せて使用すると、プログラム的な認可方法が可能になるため、リソースに対するファイングレイン制御が可能になります。
このモデルでは、認可ポリシーで次の情報が指定されます。
アプリケーションおよびエンタープライズ・グループ。
ロールのメンバー。これは他のロールまたはユーザーとすることもできます。
ユーザー、グループおよびコード・ソースに付与されるパーミッション。ユーザーおよびグループの場合は、これによってアクセスが許可されるユーザーまたはグループのメンバーが決定されます。コード・ソースの場合は、これによって、コードが実行できるアクションが決定されます。
このモデルを使用してプログラミングする場合、機密性の高いコード行の前に、現在のユーザーがそのコードにアクセスするための適切なパーミッションを付与されているかどうかを確認するコールが挿入されます。ユーザーが適切なパーミッションを持っている場合は、コードが実行されます。適切なパーミッションが与えられていない場合、コードは例外をスローします。
Java 2の標準パーミッションの詳細は、http://java.sun.com/javase/6/docs/technotes/guides/security/permissions.htmlを参照してください。
JavaEEの認可モデルはロールに基づき、コンテナによって管理されます。ポリシーによってユーザーとロールにパーミッションが割り当てられ、コンテナによってそのパーミッションが適用されて、リソースが保護されます。
コンテナは、内部で稼動中のアプリケーションに対して、宣言的およびプログラム的という2つの方法で認可を提供できます。次の各項で、これらのトピックについて説明し、例を示します。
この認可方法では、URLベースのリソース(サーブレットやページなど)やEJBのメソッドに対するアクセスを制御することができます。
宣言的な認可を構成する基本手順は、次のとおりです。
標準のデプロイメント・ディスクリプタで、保護するリソース(Web URL、EJBメソッドなど)と、そのリソースへのアクセス権を持つ論理ロールを指定します。
または、JavaEE 1.5は注釈をサポートしているため、デプロイメント・ディスクリプタではなくコード注釈を使用します。
固有デプロイメント・ディスクリプタ(web.xmlなど)では、手順1で定義した論理ロールをエンタープライズ・グループにマップします。
詳細は、『Oracle Fusion Middleware Enterprise JavaBeans Developer's Guide for Oracle Containers for Java EE』の「セキュリティ・サービスの使用方法」を参照してください。
プログラム的な認可では、宣言的な方法よりもきめ細かい認可を提供できます。プログラム的な認可では、アプリケーション・コードでメソッドisUserInRole(サーブレットおよびJSPの場合)またはメソッドisCallerInRole(EJBの場合)を呼び出す必要があります。これらはどちらも標準のJava APIに用意されています。
これらのメソッドは、認可の決定に関してロール・メンバーシップにある程度は依存しますが、これらの使用により、ユーザーは、リソース・レベル(EJBのメソッドまたはURL)のアクセス制御に限定されることがないため、よりきめ細かく認可の決定を制御できるようになります。
この例では、メソッドisUserInRoleをコールするサーブレットを示しています。サーブレットをパッケージ化しているEARファイルには構成ファイルweb.xmlおよびweblogic-application.xmlが含まれており、これらのファイルには次の構成の断片が含まれていることを前提とします。
web.xml
<!-- security roles --> <security-role> <role-name>sr_developer</role-name> </security-role>
weblogic-application.xml
次のスニペットは、ユーザーweblogicとセキュリティ・ロールsr_developer間のマッピングを示しています。
<wls:security-role-assignment> <wls:role-name>sr_developer</wls:role-name> <wls:principal-name>weblogic</wls:principal-name> </wls:security-role-assignment>
isUserInRoleを呼び出すコード例
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
public class PolicyServlet extends HttpServlet {
public PolicyServlet() {
super();
}
public void init(ServletConfig config)
throws ServletException {
super.init(config);
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
final ServletOutputStream out = response.getOutputStream();
response.setContentType("text/html");
out.println("<HTML><BODY bgcolor=\"#FFFFFF\">");
out.println("Time stamp: " + new Date().toString());
out.println( "<br>request.getRemoteUser = " + request.getRemoteUser() + "<br>");
out.println("request.isUserInRole('sr_developer') = " + request.isUserInRole("sr_developer") + "<br>");
out.println("request.getUserPrincipal = " + request.getUserPrincipal() + "<br>");
out.println("</BODY>");
out.println("</HTML>");
}
}
JAASおよびOPSS認可では、クラスを実行環境にロードするときに、そのクラスで実行できる操作を制御することが基本になっています。
この項の内容は次のとおりです。
Oracle Fusion Middlewareは、次の標準クラスでメソッドcheckPermissionの使用をサポートしています。
java.lang.SecurityManager
java.security.AccessController
また、Oracle Fusion Middlewareは、次のクラスでメソッドcheckPermissionの使用もサポートしています。
oracle.security.jps.util.JpsAuth
checkPermissionは、(前述の2つではなく)クラスJpsAuthで使用することをお薦めします。これは、このクラスのほうがデバッグ・サポートとパフォーマンスにおいて優れており、監査もサポートされるためです。
静的メソッドAccessController.checkPermissionは、デフォルトのアクセス制御コンテキスト(スレッド作成時に継承されるコンテキスト)を使用します。その他のコンテキストでパーミッションを確認するには、特定のAccessControlContextインスタンスでインスタンス・メソッドcheckPermissionを呼び出します。次の例に、このメソッドの使用法を示します。
//create permission
FilePermission perm = new FilePermission("/home/developer/foo.txt","read");
//check permission
AccessController.checkPermission(perm);
次の表に示すように、メソッドcheckPermissionはJAASモード(第15章「サーブレット・フィルタとEJBインターセプタの構成」を参照)の値に従って動作します。
表18-2 JAASモードに応じたcheckPermissionの動作
| JAASモードの設定 | checkPermission |
|---|---|
|
オフまたは未定義 |
有効なセキュリティ・ポリシーに基づくコードベースのセキュリティを実施します。サブジェクトベースのセキュリティに対するプロビジョニングはありません。 |
|
doAs |
|
|
doAsPrivileged |
コードベースおよびサブジェクトベースのセキュリティを組み合せて実施しますが、NULLアクセス制御コンテキストを使用します。この設定は、サブジェクトベースのセキュリティのみであることを意味します。 |
|
subjectOnly |
パーミッションの評価時に、プリンシパルにかかわる権限付与のみが考慮されます(コードベースにかかわる権限付与は無視されます)。 |
クラスoracle.security.jps.util.JpsAuthは監査をサポートしているため、アプリケーションはパーミッション・チェックの成功と失敗を監査できます。
さらに、次のシステム・プロパティの設定
-Djps.auth.debug.enable=true -Djps.auth.debug.enable.verbose =true
によって、パーミッション・チェックに失敗したプリンシパルまたはコードソースに関する情報が提供されます。さらに、パーミッション・チェックに合格したプリンシパルまたはコードソースの詳細については、コンテナがセキュリティ・マネージャを使用して起動していない場合でも提供されます。
Oracle Fusion Middlewareは、標準クラスjavax.security.auth.Subjectで、doAsメソッドとdoAsPrivilegedメソッドをサポートします。
ただし、これらのメソッドは、パフォーマンスが高く、監査機能も備えているため、oracle.security.jps.util.JpsSubjectクラスで使用することをお薦めします。
この項では、パーミッション・チェックとポリシー管理について記述しているコード例を示します。
次の例は、パーミッションをチェックするサーブレットを示しています。サーブレットをパッケージ化しているEARファイルに構成ファイルjazn-data.xmlおよびweb.xmlが含まれていることが前提となります。
jazn-data.xml
アプリケーションには次のポリシーが含まれています。
<policy-store>
<applications>
<application>
<!-- application name or application context -->
<name>PolicyServlet</name>
<app-roles>
<app-role>
<name>developer</name>
<display-name>application role developer</display-name>
<class>oracle.security.jps.service.policystore.ApplicationRole</class>
<members>
<member>
<class>weblogic.security.principal.WLSUserImpl</class>
<name>weblogic</name>
</member>
</members>
</app-role>
</app-roles>
<!-- application specific jaas policy -->
<jazn-policy>
<grant>
<grantee>
<principals>
<principal>
<class>oracle.security.jps.service.policystore.ApplicationRole</class>
<name>developer</name>
</principal>
</principals>
</grantee>
<permissions>
<permission>
<class>java.lang.RuntimePermission</class>
<name>getClassLoader</name>
</permission>
</permissions>
</grant>
</jazn-policy>
</application>
</applications>
</policy-store>
web.xml
フィルタJpsFilterは次のように構成されます。
<web-app>
<display-name>PolicyTest: PolicyServlet</display-name>
<filter>
<filter-name>JpsWlsFilter</filter-name>
<filter-class>oracle.security.jps.ee.http.JpsFilter</filter-class>
<init-param>
<param-name>application.name</param-name>
<param-value>PolicyServlet</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>JpsWlsFilter</filter-name>
<servlet-name>PolicyServlet</servlet-name>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>...
コード例
次の例では、Subject.doAsPrivilegedがJpsSubject.doAsPrivilegedによって置換される場合があります。
import javax.security.auth.Subject;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.*;
import java.util.Date;
import java.util.PropertyPermission;
import java.io.FilePermission;
public class PolicyServlet extends HttpServlet {
public PolicyServlet() {
super();
}
public void init(ServletConfig config)
throws ServletException {
super.init(config);
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
final ServletOutputStream out = response.getOutputStream();
response.setContentType("text/html");
out.println("<HTML><BODY bgcolor=\"#FFFFFF\">");
out.println("Time stamp: " + new Date().toString());
out.println( "<br>request.getRemoteUser = " + request.getRemoteUser() + "<br>");
out.println("request.isUserInRole('sr_developer') = " + request.isUserInRole("sr_developer") + "<br>");
out.println("request.getUserPrincipal = " + request.getUserPrincipal() + "<br>");
Subject s = null;
s = Subject.getSubject(AccessController.getContext());
out.println("Subject in servlet " + s);
out.println("<br>");
final RuntimePermission rtPerm = new RuntimePermission("getClassLoader");
try {
Subject.doAsPrivileged(s, new PrivilegedAction() {
public Object run() {
try {
AccessController.checkPermission(rtPerm);
out.println("<br>");
out.println("CheckPermission passed for permission: " + rtPerm+ " seeded in application policy");
out.println("<br>");
} catch (IOException e) {
e.printStackTrace();
printException ("IOException", e, out);
} catch (AccessControlException ace) {
ace.printStackTrace();
printException ("Accesscontrol Exception", ace, out);
}
return null;
}
}, null);
} catch (Throwable e) {
e.printStackTrace();
printException("application policy check failed", e, out);
}
out.println("</BODY>");
out.println("</HTML>");
}
void printException(String msg, Throwable e, ServletOutputStream out) {
Throwable t;
try {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
e.printStackTrace(pw);
out.println("<p>" + msg + "<p>");
out.println("<code>");
out.println(sw.getBuffer().toString());
t = e;
/* Print the root cause */
while ((t = t.getCause()) != null) {
sw = new StringWriter();
pw = new PrintWriter(sw, true);
t.printStackTrace(pw);
out.println("<hr>");
out.println("<p> Caused By ... </p>");
out.println(sw.getBuffer().toString());
}
out.println("</code><p>");
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
次のコード例は、ポリシーの管理を示しています。
import oracle.security.jps.JpsContext;
import oracle.security.jps.JpsContextFactory;
import oracle.security.jps.service.policystore.ApplicationPolicy;
import oracle.security.jps.service.policystore.PolicyStore;
import oracle.security.jps.service.policystore.info.AppRoleEntry;
import javax.security.auth.AuthPermission;
import javax.security.auth.Subject;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.AccessController;
import java.security.Permission;
import java.security.Principal;
import java.util.Date;
import java.util.List;
public class PolicyServlet extends HttpServlet {
public PolicyServlet() {
super();
}
public void init(ServletConfig config)
throws ServletException {
super.init(config);
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
final ServletOutputStream out = response.getOutputStream();
response.setContentType("text/html");
out.println("<HTML><BODY bgcolor=\"#FFFFFF\">");
out.println("Time stamp: " + new Date().toString());
out.println( "<br>request.getRemoteUser = " + request.getRemoteUser() + "<br>");
out.println("request.isUserInRole('sr_developer') = " + request.isUserInRole("sr_developer") + "<br>");
out.println("request.getUserPrincipal = " + request.getUserPrincipal() + "<br>");
Subject s = null;
s = Subject.getSubject(AccessController.getContext());
out.println("Subject in servlet " + s);
out.println("<br>");
try {
JpsContextFactory ctxFact = JpsContextFactory.getContextFactory();
JpsContext ctx = ctxFact.getContext();
final PolicyStore policyStore = ctx.getServiceInstance(PolicyStore.class);
ApplicationPolicy policyServletPolicy
= policyStore.getApplicationPolicy("PolicyServlet");
//get application role developer
List<AppRoleEntry> developerAppRoleList = policyServletPolicy.searchAppRoles("developer");
AppRoleEntry approleEntry = developerAppRoleList.get(0);
Principal pr = approleEntry.getPrincipal();
//grant permissions to this role
Principal[] principals = new Principal[]{pr};
AuthPermission doAsPerm = new AuthPermission("authPerm");
final Permission[] perms = new Permission[]{doAsPerm};
out.println("<br> Attempting to grant principal " + pr + " Permissions in application Policy ");
policyServletPolicy.grant(principals, null, perms);
out.println("<br> Granted permissions");
Subject subject = new Subject();
subject.getPrincipals().add(pr);
out.println("<br> principal has permissions ?" + policyServletPolicy.hasPermission(subject, doAsPerm));
} catch(Exception e) {
e.printStackTrace();
printException("Exception in Policy Management " , e, out);
}
out.println("</BODY>");
out.println("</HTML>");
}
void printException(String msg, Throwable e, ServletOutputStream out) {
Throwable t;
try {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
e.printStackTrace(pw);
out.println("<p>" + msg + "<p>");
out.println("<code>");
out.println(sw.getBuffer().toString());
t = e;
/* Print the root cause */
while ((t = t.getCause()) != null) {
sw = new StringWriter();
pw = new PrintWriter(sw, true);
t.printStackTrace(pw);
out.println("<hr>");
out.println("<p> Caused By ... </p>");
out.println(sw.getBuffer().toString());
}
out.println("</code><p>");
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}