- 素のコンポーネントを作って、よそから自由に使える事を確認する。
- このコンポーンネントに権限チェックを追加して、勝手に使えなくする。
- 認証された権限保持者が正しく実行すると、コンポーネントが使えるのを確認。
- これらのプロジェクトを 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 件のコメント:
コメントを投稿