- 素のコンポーネントを作って、よそから自由に使える事を確認する。
- このコンポーンネントに権限チェックを追加して、勝手に使えなくする。
- 認証された権限保持者が正しく実行すると、コンポーネントが使えるのを確認。
- これらのプロジェクトを AspectJで リファクタする
confidential-component
という名前の Java Project を作る。- 以下のクラスを作る。
package study.jaas_aop.component; public class ConfidentialComponent { public String summary() { return "summary"; } public String detail() { return "detail"; } }
- client という名前の Java Projectを作る。
- Java Build Path の Projectタブ で、
confidential-component
プロジェクトへの参照を指定する。 - もう一つ、以下のクラスを作る。
package study.jaas_aop.client; import study.jaas_aop.component.ConfidentialComponent; public class App { public static void main(String[] args) { ConfidentialComponent comp = new ConfidentialComponent(); System.out.println(comp.summary()); System.out.println(comp.detail()); } }
- 実行して、"summary" "detail" と2行表示されるのを確認する。
- confidential-component プロジェクトで、以下のようなパーミッションクラス MyPermission を作る
package study.jaas_aop.component; import java.security.BasicPermission; public class MyPermission extends BasicPermission { public MyPermission(String name) { super(name); } }
- ConfidentialComponent の summary()、detail() で checkPermission()する
public String summary() { AccessController.checkPermission(new MyPermission("summary")); return "summary"; } public String detail() { AccessController.checkPermission(new MyPermission("detail")); return "detail"; }
- 実行して、"summary"でアクセス例外が出るのを確認するException in thread "main" java.security.AccessControlException: access denied (study.jaas_aop.component.MyPermission summary)
- summary(), detail() 両メソッドの呼び出しを PrivilegedAction でラップして、Subject.doAsPrivileged()経由で実行
- Subject は、LoginContext から得る
- Subject を返す前に、LoginContext の login() メソッドが実行されている必要がある。
public static void main(String[] args) throws Exception { LoginContext lc = new LoginContext("study"); lc.login(); Subject subject = lc.getSubject(); final ConfidentialComponent comp = new ConfidentialComponent(); Subject.doAsPrivileged(subject, new PrivilegedAction<Object>() { @Override public Object run() { System.out.println(comp.summary()); return null; } }, null); Subject.doAsPrivileged(subject, new PrivilegedAction<Object>() { @Override public Object run() { System.out.println(comp.detail()); return null; } }, null); }LoginContext のコンストラクタに渡している文字列"study"は、ログイン設定ファイル内のエントリ名で、これをキーとしてログインモジュール(後述)が関連付けられる。ここではファイル名
my_login.config
とし、client プロジェクトディレクトリ直下に置く。
study { study.jaas_aop.lm.DummyLoginModule required; };このログイン設定ファイが実行時に読み込まれるように、[Run Configuration]の[VM arguments]で以下のように指定する。
-Djava.security.auth.login.config=my_login.config
◆ログインモジュール作成
続いて上記ファイルで指定した、DummyLoginModule を作る。ただし、今回はクライアント目線中心でやってるので、本格的な認証ロジックは省略したダミーのログインモジュールとする。つまり LoginContext.login()が呼ばれると、常に固定 ユーザ"manager"がログイン成功するという筋書きとする。(後で"manager"を"worker"に変える。)
プロジェクト名 login-module で Java Project を作成し、以下の2クラスを書く
package study.jaas_aop.lm; import java.util.Map; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; public class DummyLoginModule implements LoginModule { private static final String USER_NAME = "manager"; private Subject subject = null; public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { this.subject = subject; } public boolean login() { return true; } public boolean commit() throws LoginException { subject.getPrincipals().add(new DummyPrincipal(USER_NAME)); return true; } public boolean abort() throws LoginException { return false; } public boolean logout() throws LoginException { return false; } }
package study.jaas_aop.lm; import java.security.Principal; public class DummyPrincipal implements Principal { private String name; public DummyPrincipal(String name) { this.name = name; } public String getName() { return name; } }上記コードを簡単に説明すると、App.main() での LoginContext.login() 実行中に、DummyLoginModule のinitialize(), login(), commit()が順に呼ばれ、最終的に ユーザ"manager"に対応づく Principal が、Subject に関連付けられるという事になる。(注意:あくまでもダミーなので、ちゃんと本物の LoginModule を書くときの参考にはならない。) さて、これでログインモジュールができたが、"manager"が認証されたとして、何をする事が許されているのかを、さらに定義する必要がある。これは プロジェクトディレクトリ直下に
security.policy
ファイルを置いて、そこで ConfidentialComponent のメソッドへのアクセス権を指定する事にする。
grant Principal study.jaas_aop.lm.DummyPrincipal "manager" { permission study.jaas_aop.component.MyPermission "summary"; permission study.jaas_aop.component.MyPermission "detail"; };この
security.policy
が実行時に読まれるようにするためには、VM argument で以下の指定を追加する。 -Djava.security.policy=security.policy
あと、[Configure Build Path] で、client プロジェクトから login-moduleプロジェクトへの参照も設定しておく。
以上で、準備ができたので動かしてみる。ちゃんと"summary"と"detail"の2行が表示されているはず。
ここで manager の他に、もう一人 worker というユーザもいる状況を試してみる。この人には summary()へのアクセスは許可されているが、detail()へのアクセスは許されていない。
security.policy
中の manager プリンシパルの下に、以下を追記する。
grant Principal study.jaas_aop.lm.DummyPrincipal "worker" { permission study.jaas_aop.component.MyPermission "summary"; };DummyLoginModule の USER_NAME を"worker"に変更して、再度試してみると、"summary"と表示された行の下に、AccessControlException がスタックトレースされ、"access denied (~~detail)"と言うメッセージが出ているはず。 とまあこんな感じでsecurity.policy ファイルの設定が効いているのが分かる。以後、
security.policy
の効き目がはっきりわかるように、USER_NAME は"worker"のままにしておく。
■ 4. AspectJでリファクタする
認証コードを追加した結果、一番最初の App.main()と比べると、関心の異なるコードが入り乱れてかなりゴチャゴチャしている。これをまず直してみる。- client プロジェクトのコンテキストメニューから、[Convert to AspectJ Project]を実行する
- 新規アスペクト"AuthAspect"を作成する。
package study.jaas_aop.client; import java.security.PrivilegedAction; import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import study.jaas_aop.client.App; import study.jaas_aop.component.ConfidentialComponent; public aspect AuthAspect { private Subject subject; public pointcut upfontLoginPoint() :execution(void App.main(String[])); public pointcut authOperationCalls() :call(public * ConfidentialComponent.*(..)); before() : upfontLoginPoint() { try { LoginContext lc = new LoginContext("study"); lc.login(); subject = lc.getSubject(); } catch (LoginException ex) { throw new RuntimeException(ex); } } Object around() : authOperationCalls() && !cflowbelow(authOperationCalls()) { return Subject.doAsPrivileged(subject, new PrivilegedAction<Object>() { public Object run() { return proceed(); } }, null); } }
- App.main()を、元の認証コードのないやつに戻す。
- ユーザ"worker"で実行してみると、summary() 成功/detail()失敗となり、ちゃんと認証が利いているのがわかる。
confidential-component
側も AOP でリファクタしておく。
confidential-component
プロジェクト上で[Convert to AspectJ Project]を実行- 新規アスペクト PermAspect を作成
package study.jaas_aop.component; import java.security.AccessController; public aspect PermAspect { public pointcut authOperations() :execution(public * ConfidentialComponent.*(..)); before() : authOperations() { String name = thisJoinPointStaticPart.getSignature().getName(); AccessController.checkPermission(new MyPermission(name)); } }
- ConfidentialComponent の checkPermission() を呼んでいる行を削除して、一番最初のやつにもどす。
- 実行して、挙動に変化が無く正しく動いている事を確認する。
0 件のコメント:
コメントを投稿