kagamihogeの日記

kagamihogeの日記です。

Java Authentication Guide with Apache Shiroをテキトーに訳した

Apache Shiroのチュートリアルの一部でAuthenticationに関する部分の https://shiro.apache.org/java-authentication-guide.html をテキトーに訳した。

Java Authentication Guide with Apache Shiro

認証とは本人確認のプロセスです。アプリケーション側ではユーザが自身を誰と言ってきているのかを確認します。確認のためには、アプリケーションシステムが理解できる形式かつ信用する、本人確認用の何かをユーザは提供する必要があります。

このガイドの目的はJavaでの認証をShiroで実行する方法の概要を示すことです。Shiroの10 Minute Tutorialをまだ読んでいない場合はまずそちらに時間を取り、Shiroの基本的知識を身につけてください。

Terminology you’ll need

  • サブジェクト(Subject) - アプリケーションユーザのセキュリティ視点でのユーザの'ビュー'。サブジェクトになるのは、人間・サードパーティプロセス・アプリケーションに接続してくるサーバ・cron job、など。基本的に、アプリケーションと通信する人やモノのことを指す。
  • プリンシパルPrincipals) - サブジェクトで識別される属性。姓・名・社会保障番号(social security number)・ユーザ名など。
  • クレデンシャル(Credentials)- 本人確認に使われる秘密データ。パスワード・生体データ・x509証明書、など。
  • レルム(Realms) - セキュリティ固有のDAO、いわゆるdata access objectで、バックエンドのデータソースにアクセスするソフトウェアコンポーネント。たとえばLDAPでユーザ名とパスワードがある場合、LDAPにアクセスするLDAP Realmを作ることになる。開発者に必要な作業はバックエンドのデータソース毎にレルムを作成することで、Shiro側でそれらのレルムを呼び出しします。

*1

How to Authenticate in Java with Shiro

Shiroフレームワークとその他の同目的のフレームワークで、Javaの認証プロセスは三つの個々のステップで構成されます。

Steps

  1. サブジェクトのプリンシパルとクレデンシャルを取得する。
  2. プリンシパルとクレデンシャルを認証システムにサブミットする。
  3. アクセス許可か、認証をリトライするか、アクセスをブロックする。

以下にShiroでこれを行うためのコードを具体的に示します。

Step 1 - Collect the subject’s principals and credentials

//最も一般的な例。
//文字列のユーザ名とパスワードをシステム固有の方法(HTTPリクエスト・GUIなど)
//で受け取る。
UsernamePasswordToken token = new UsernamePasswordToken( username, password );

//”Remember Me” built-in, just do this: 
token.setRememberMe(true);

上記のよくある場合の例では、UsernamePasswordTokenというクラスを使用しています。このクラスはShiroで最も使われている認証トークンです。

Javaアプリケーションで何らかの方法で受け取るユーザ名とパスワードを紐付けるのにこのトークンを使用します。webフォーム・HTTPヘッダー・コマンドラインなど経由でユーザ名とパスワードはサブミットされます。Shiroは、受け取り方については関知せず、プロトコルとは無関係です。

上記の例では、アプリケーションがユーザを保持するようにしています。トークン生成後、'Remember-me'をtrueにすることでShiroの組み込み機能を使用しています。この機能を使うにはトークンのsetRememberMe() を呼びます。

Step 2 - Submit the principals and credentials to an authentication system.

上記のステップでトークンに情報を入れてユーザを記憶するよう設定しました。次のステップは認証プロセスで、ここで認証システムにトークンをサブミットします。認証システムをShiroで作るには、セキュリティ用のDAOであるRealmsを作成します。レルムの詳細についてはShiro Realm Guideを参照してください。

Shiroでは、この部分をなるべく平易で素早く作れるようにしており、一行のJavaコードで作れます。

//Shiroではたいていの場合、サブジェクトには現在実行中のユーザを取得したいことと思われます。
Subject currentUser = SecurityUtils.getSubject();

//ログインメソッドにユーザ名とパスワードのトークンを渡すことでサブジェクトを認証する。
currentUser.login(token);

まず、サブジェクトの形で現在実行中のユーザを取得します。サブジェクトはユーザのセキュリティ視点でのビューで、人・プロセス・cron jobなど、が該当します。Shiroでは、現在実行中のスレッドに利用可能なサブジェクトが常に存在します。サブジェクトの考え方は、Shiroおよび同種のフレームワークのコア部分で、サブジェクトに関する処理が中心となります。このサンプルでは、サブジェクトのインスタンス名をcurrentUserとしています。

サブジェクトを得るには、ShiroのコアAPIの一つSecurityUtilsを使います。getsubject()で現在実行中のユーザが得られます。そのメソッドにより、システムとやり取りする現在のユーザを表すサブジェクトインスタンスが得られます。この時点では、サブジェクトcurrentUesrはアノニマスです。ユーザ固有の情報は何も関連付けられていません。

これでユーザが得られたので、login()を呼び、Step 1で作成したトークンをサブミットすることで、認証を行います。

Step 3 - Allow access, retry authentication, or block access

繰り返しになりますが、ここでは一つのメソッドを呼ぶだけです。login()メソッド呼び出しが正常終了する場合、ユーザはログインしてユーザアカウントやユーザ固有の情報と関連付けられます。その後は、ユーザはアプリケーションで使用可能となり、このサンプルでは"Remember Me"をセットしているので、セッション継続中はユーザ固有の情報を保持し続けます。

認証処理中に何らかの理由で失敗した場合はどうなるのでしょうか。パスワードを間違えたり何度もシステムアクセスするとアカウントロックされるのでしょうか? そのような場合、Shiroは例外をスローします。Shiroには豊富な階層の例外が用意されています。

try {
    currentUser.login(token);
} catch  ( UnknownAccountException uae ) { ...
} catch  ( IncorrectCredentialsException ice ) { ...
} catch  ( LockedAccountException lae ) { ...
} catch  ( ExcessiveAttemptsException eae ) { ...
} ...  your own ...
} catch ( AuthenticationException ae ) {
    //unexpected error?
}
//No problems, show authenticated view…

メソッド呼び出しをtry/catchブロックでラップしてすべての種類の例外をキャッチし、それぞれに応じた処理を行えます。Shiroが提供する多数の例外だけでなく、必要に応じて自前のカスタム例外を作成できます。詳細についてはAuthenticationExceptionを参照してください。

Handy Hint
セキュリティのベストプラクティスに、ユーザにはログイン失敗の詳細を見せない、というものがあります。これはシステム侵入を目論むアタッカーに情報を与えないためです。

“Remember Me” Support

上述のサンプルで見た通り、Shiroは一般的なログイン処理に加えて"remember me"の概念をサポートしています。

Shiroでは、SubejctオブジェクトはisRemembered()isAuthenticated()の二つのメソッドをサポートしています。

“remembered"状態のサブジェクトは(アノニマスでは無い)識別状態とプリンシパルと呼ばれる識別属性を持ち、プリンシパルは以前のセッション中に正常終了した認証処理で保存されます。

認証サブジェクトとは、現在のセッションにおいて本人確認が行われた、という意味になります。

Warning
サブジェクトがremembered状態だとしても、サブジェクトが認証されているという意味にはなりません。

Remembered vs Authenticated

Shiroにおける重要なポイントとして、remembered状態のサブジェクトは認証状態のサブジェクトではありません。認証プロセスはユーザが誰なのかを確認するものなのでisAuthenticated()に対するチェックはisRememberedよりも厳密なチェックになります。あるユーザがrememberedのみの場合、そのrememberedの情報はおそらくそのユーザのものだろうとシステムに示唆はしますが、実際のところ、rememberedのサブジェクトが現在アプリケーションを使用しているユーザかどうかの絶対的な保証はしません。サブジェクトが一度認証されたら、rememberedのみかどうかをそれ以降気にする必要はありません。その理由は、認証済みの情報は現在のセッションで本人確認されたものだからです。

customized viewsなどのremembered principals*2に基づくユーザ固有のロジックを、アプリケーションの大部分は実行可能ですが、認証処理が正常終了してユーザが正しく本人確認されるまで機密性の高い操作は実行すべきではありません。

例えば、isAuthenticated()に常に依存すべき金融情報にアクセス可能なサブジェクトの場合、認証済みの情報であると保証するためにisRemembered()はチェックしません。

isAuthenticatedとisRememberedの違いが重要な理由を例を示しながら解説します。

ここで、Amazon.comを使っているとしましょう。ログインしてショッピングカートに何冊かの本を追加したあと、そのまま何日かが経ちました。当然セッションは期限切れでログアウトしています。しかし、Amazonはユーザを記憶(“remembers”)しているので、ユーザの名前を表示し、本のパーソナライズドされたレコメンドも表示します。Amazonは、isRemembered()TRUEを返すでしょう。ここで、保存済みのクレジットカードの一つを使おうとしたりアカウント情報を変更しようとするとどうなるでしょうか? AmazonisRemembered() = TRUEでユーザを記憶しているものの、isAuthenticated()=FALSEだと、ユーザが本当にそのユーザ本人かどうかは確実ではありません。よって、機密度の高いアクションをユーザが実行可能にする前に、Amazonはログイン画面経由の認証プロセスを強制することで本人確認を行います。ログイン完了すると、本人確認が終了したのでisAuthenticated()=TRUEになります。

上記のシナリオはwebでは極めて一般的なのでその機能はShiroに組み込まれています。

Logging Out

最後に、ユーザがアプリケーション使用後は、ログアウト可能です。Shiroでは単一のメソッド呼び出しで簡単にログアウトを作れます。

currentUser.logout(); //すべての認証情報を削除してセッションを無効化する。

Shiroをログアウトするとユーザセッションをクローズしてサブジェクトインスタンスから関連付けられた認証情報を削除します。web環境でRememberMeを使う場合で、.logout()すると、デフォルトではブラウザからRememberMeのクッキーを削除します。

Lend a hand with documentation

Apache Shiroで何かを作る際にこのドキュメントが役に立つことを願いますが、コミュニティが成長すればドキュメントも常に拡張していきます。もしShiroプロジェクトに興味がある場合、必要だと感じたドキュメントの追加・修正・収集をお願いしたいと思います。ほんの少しの助けであってもコミュニティは前進し、結果としてShiroも前進します。

ドキュメントをコントリビュートする最も簡単な方法はこのページ下のEditリンク*3をクリックしてプルリクエストをサブミットするか、User ForumあるいはUser Mailing Listに送信します。

*1:上の用語はこのエントリ内では上記通りカタカナ表記にする。あんまセキュリティに詳しくないんだけど、軽くぐぐった感じ、だいたいカタカナ表記でOKっぽいんで。

*2:remembered principals, such as customized viewsが原文。よくわからんけどユーザが表示項目を追加したり並び順変えたりとかそういうもののこと?

*3:言うまでもなく、原文ページのフッター部分にあるEditリンクのこと

SQL Developer 4.2 Early Adopter 2のInstance ViewerのTop SQL

www.thatjeffsmith.com

SQL Developerには4.1にinstance viewerというDBの現在の実行状態をグラフィカルに表示するツールが追加されている。4.2では更にTop SQLという機能が追加されるらしい。ので、実際に試してみる。instance viewerについは SQL Developer 4.1の新機能Instance Viewerを試す など参照。

環境

つかってみる

表示 -> DBA -> インスタンス・ビューア、から起動する。

f:id:kagamihoge:20170122172746j:plain

これのTOP SQLというパネルが追加されるもの。

10 Minute Tutorial on Apache Shiroをテキトーに訳した

Apache Shiroはじめて見たけどよくわからんな~ということでチュートリアルを読んで訳した。

https://shiro.apache.org/10-minute-tutorial.html

10 Minute Tutorial on Apache Shiro

Introduction

これはApache Shiroの10分チュートリアルです。

シンプルなチュートリアルに素早く目を通すことで、開発者はアプリケーションでShiroを使う方法を完全に理解するでしょう。また、所要時間は10分程度です。

Overview

Apache Shiroとは何でしょうか?

Apache Shiroは強力で使いやすいJavaのセキュリティフレームワークで開発者に直感的かつ包括的な機能を提供します。機能には、認証(authentication)・認可(authorization)・暗号化・セッション管理、があります。

実際のところ、Apache Shiroはアプリケーションに出来る限り浸食しないようにしつつ、セキュリティが直面するすべてを管理します。インタフェース駆動設計とオブジェクト指向の原則で作られており、カスタム可能だろうと思われる個所の振る舞いはどこでも変えられます。とはいえすべて妥当なデフォルトで、なるべくアプリケーションのセキュリティには手を出したくはありません。少なくとも我々はそれに向けて努力をしています。

Apache Shiroでは何が出来るのでしょうか?

いろいろです。とはいえQuickStartをあまり膨らませたくはありません。もしApache Shiroで出来ることについて知りたい場合はFeaturesページを参照してください。また、Apache Shiroの成り立ちや利点について興味がある場合は、Shiro History and Missionを参照してください。

それでは、チュートリアルを始めていきます。

Note
Shiroは、単純なコマンドラインアプリケーションから大規模なエンタープライズwebアプリケーションやクラスタアプリケーションなど、任意の環境で動作可能ですが、このQuickStartではシンプルな'main'メソッドのサンプルを使用するので、APIの雰囲気を見れるでしょう。

Download

  • JDK 1.6+およびMaven 3.0.3+をインストール。
  • Downloadページから最新の“Source Code Distribution”をダウンロードする。このサンプルでは、1.3.2 release distributionを使用。
  • ソースパッケージをUnzip.
$ unzip shiro-root-1.3.2-source-release.zip
$ cd shiro-root-1.3.2/samples/quickstart
  • QuickStartを実行。
$ mvn compile exec:java

上のtargetは何行かのログを表示してから終了するのでそこから何をしているのかを知ることができます。クイックスタートを読み進めながら、適宜samples/quickstart/src/main/java/Quickstart.javaのコードを参照してください。ファイルを修正して実行するにはmvn compile exec:javaコマンドを実行してください。

Quickstart.java

上に示したQuickstart.javaファイルにはAPIに慣れるためのすべてのコードが含まれています。それでは部分ごとに詳しく見ていくことで、コードが何をしているのかを理解していきます。

おおむねすべての環境下で、以下のようなコードにより現在実行中のユーザを取得します。

Subject currentUser = SecurityUtils.getSubject();

(https://shiro.apache.org/static/current/apidocs/org/apache/shiro/SecurityUtils.html)[SecurityUtils].getSubject()で、現在実行中のSubjectを取得します。サブジェクト(Subject)とは、アプリケーションユーザのセキュリティ視点での"ビュー"です。我々としては名は体を表す'User'にしたかったのですが、すでに大抵のアプリケーションはそこで固有のUserクラスやフレームワークを持っていたため、それらとコンフリクトしないよう断念しました。なお、セキュリティの世界では、Subjectは広く通用する専門語です。というわけで、続きにいきます。

スタンドアローンアプリケーションでのgetSubject()はそのアプリケーション固有の環境でのユーザベースのSubjectを返し、サーバ環境(例:webアプリケーション)ではカレントスレッドもしくは到着リクエストに関連付けられたユーザーベースのgetSubject()を返します。

ではgetSubject()が得られると何が出来るのでしょうか?

アプリケーションの現在セッションでユーザが何がしかの値を使えるようにしたい場合、まずセッションを取得します。

Session session = currentUser.getSession();
session.setAttribute( "someKey", "aValue" );

SessionはShiro固有のインスタンスで、いわゆるHttpSessionで使用しているものを提供しますが、ちょっとした違いと大きな違いがあります。ShiroはHTTP環境を必要としません。

webアプリケーション内にデプロイする場合、デフォルトではSessionHttpSessionベースになります。しかし、非web環境、たとえばこのQuickstartのような場合、Shiroは自動的にデフォルトでEnterprise Session Managementを使用します。つまり、いかなるレイヤでも、デプロイ環境に関わらず、アプリケーション内では同じAPIを使える、ということです。これにより、セッションを必要とするアプリケーションでHttpSessionEJB Stateful Session Beansの使用を強制される必要がないため、新しい可能性が開けます。また、任意のクライアント技術でセッションデータを共有できます。

いま、SubjectとそのSessionが得られました。それで、実際に有用な検査などの機能は何かあるのでしょうか? ロールとパーミッションを検査するような。

それについては、既知のユーザに対する検査のみが可能です。上述のSubjectインスタンスは現在のユーザを表現しますが、現在のユーザとはいったいなのでしょうか? これはアノニマスになります、一度はログインするまでは。以下を見てみます。

if ( !currentUser.isAuthenticated() ) {
    //ユーザプリンシパルとクレデンシャルをGUI固有の方法で取得する。
    //たとえば、username/passwordのHTMLフォーム・X509 certificate・OpenIDなど。
    //ここでは最も一般的な例としてusername/passwordを使用している
    //(ネタ元の映画はご存知だろうか?)
    UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
    //'remember me'のみ指定する必要があります(他に設定は無しno config - built in!)
    token.setRememberMe(true);
    currentUser.login(token);
}

以上のような感じで、さほど難しいところは無いでしょう。

ログインの試行が失敗した場合はどうなるのでしょう。何が起きたかを通知する固有の例外をキャッチし、その内容を処理したり何らかの反応を返すことが出来ます。

try {
    currentUser.login( token );
    //もし例外がない場合は正常パターン。
} catch ( UnknownAccountException uae ) {
    //そのシステムにusernameが存在しない。エラーメッセージを表示するのが適切かも。
} catch ( IncorrectCredentialsException ice ) {
    //passwordがマッチしなかった。再試行?
} catch ( LockedAccountException lae ) {
    //このusernameのアカウントはロックされていてログイン出来ない。
}
    ... などなど必要に応じた例外型をキャッチする ...
} catch ( AuthenticationException ae ) {
    //予期せぬ状態。何等かのエラー?
}

キャッチ可能な例外の型が多数用意されています。もしくは、Shiroが扱わないカスタム状態に対する自前の例外をスローします。詳細についれはAuthenticationException JavaDocを参照してください。

Handy Hint
セキュリティのベストプラクティスに、ユーザにはログイン失敗の詳細を見せない、というものがあります。これはシステム侵入を目論むアタッカーに情報を与えないためです。

それで、ユーザはログインしました。他に何が出来るでしょうか。

ユーザが何者かを出力してみます。

//ログインユーザを識別するプリンシパルの表示(この場合はusername)
log.info( "User [" + currentUser.getPrincipal() + "] logged in successfully." );

指定のロールを持つか持たないかも検査できます。

if ( currentUser.hasRole( "schwartz" ) ) {
    log.info("May the Schwartz be with you!" );
} else {
    log.info( "Hello, mere mortal." );
}

エンティティの指定のタイプをアクト*1するためのパーミッションを持つかどうかを検査できます。

if ( currentUser.isPermitted( "lightsaber:weild" ) ) {
    log.info("You may use a lightsaber ring.  Use it wisely.");
} else {
    log.info("Sorry, lightsaber rings are for schwartz masters only.");
}

さらに、かなり強力なインスタンスレベル(instance-level)パーミッションチェックを実行できます。これは、あるユーザが指定のタイプのインスタンスにアクセスする能力を持つかどうかを参照します。

if ( currentUser.isPermitted( "winnebago:drive:eagle5" ) ) {
    log.info("You are permitted to 'drive' the 'winnebago' with license plate (id) 'eagle5'.  " +
                "Here are the keys - have fun!");
} else {
    log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}

これくらいはカンタンに理解できることと思われます。

最後に、ユーザがアプリケーションを使用し終えたら、ログアウトします。

currentUser.logout(); // すべての識別情報を削除してセッションも無効化します。

以上がアプリケーション開発者レベルでApache Shiroを使う際のコア部分となります。なお、より優れたやり方で開発を行うにはApache Shiroの詳細に立ち入る必要がありますが、ただそれだけのことです。

なお、このような疑問を持っているかもしれません。"とはいえ、ログイン(usernameとpassword, role, permissonなど)の際にユーザーデータを取得するのは誰がやるのだろうか? それと、実行時にセキュリティチェックを実際にやるのは?" それは、あなたです。ShiroはRealmを呼び出してそのRealmをShiroのコンフィグレーションへプラグインするのでそこを実装します。

Realmをコンフィグするやり方はその実行環境に強く依存します。たとえば、スタンドアローンアプリケーション・webベースアプリケーション・SpringあるいはJava EEコンテナベースアプリケーション・もしくはそれらの組み合わせ、などです。そうした種類の設定はQuickStartでは扱いません。QuickStartはShiroのコンセプトとAPIに慣れてもらうのが目的なためです。

より詳細へと進みたい場合、Authentication GuideAuthorization Guideには確実に目を通して下さい。それから、そのほかのDocumentationに進んでください。特にReference Manualは色々な疑問に答えられると思います。mailing listに入りたい場合はこちらから。ユーザMLを通して可能な限り人々の力になることを望むコミュニティを見れます。

以上になります。Apache Shiroをエンジョイしてください。

*1:entity, type, actはshiro.iniを読んだ感じShiro固有の用語?ぽかったのでカタカナにするだけに留めた。