Acegi on Grails
(Spring Security on Grails)
Secure Your Grails Application with Acegi Security
日本語版
自己紹介
山本 剛
( 株)ニューキャスト CTO
• おもに印刷業界向けの Web アプリケーション開発
Grails ドキュメントの和訳
Grails Acegi plugin 作者
Japan Grails/Groovy User Group メンバ
アジェンダ
Acegi Security とは
Acegi on Grails の使い方
Grails のドメイン・クラスを使う
Acegi on Grails をサポートするビーン
Grails Acegi プラグイン
概要
Acegi プラグインの設定
Ajax をセキュアにする
Service をセキュアにする
Acegi (Spring) Security とは?
Spring 向けのオープンソース・セキュリ
ティ・フレームワーク
Spring AOP とサーブレット・フィルタ上に作
られている
"Acegi" ってどう発音すればいいの?
“Ah-see-gee.”
“Spring Security” に改名予定
既に 2007 年 9 月末から SVN 上のパッケージ名は
変更されている
Acegi Security が提供しているのは
セキュアなページにアクセスする前にユーザをログイン・ページに
リダイレクトする
パスワードをチェックして認証する
うまいったら, ユーザを元のセキュアなページにリダイレクトする
認証できなければ, ユーザに Access Denied ページを表示
( またはエラー・コード 403)
認証情報は Session に格納
SecurityContextHolder から情報を取得可能
ログアウト時にはセキュアなセッションのために使ったユーザ情報
を消す
などなど
Web アプリ
リソース
コントローラ
サービス
フィルタがリクエストをインタセプトしてセキュリティ
をチェック
セキュリティはURL (リクエスト・マップ) で指定
/some/url/** = ROLE_ADMIN
“/some/url/1” にアクセスするとユーザにログイン・プロンプトを表示
どうやってセキュアにしているか?
/some/url/1
アクセス 判断
リクエスト
マップ
フィルタ
どうやって使う?
まず Spring-framework ありき
acegi の jar ファイルを lib に入れる
web.xml に構成を書く
securityContext.xml にフィルタを設定
ログイン・ページを作る
web.xml の中身
<filter>
<filter-name>filterChainProxy</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>org.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filterChainProxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml
/WEB-INF/securityContext.xml
</param-value>
</context-param>
Acegi ビーンの構成を書いたコン
テキスト xml ファイル の設定
フィルタとフィルタ・マッ
ピングの設定
securityContext.xml
Acegi spring ビーンを構成する
フィルタ
• 認証の処理, セッションのハンドリング, ログアウト
マネージャ, プロバイダ
• マネージャとプロバイダはログイン/ログアウトのサービスを
実際に実装するのに使われる
動作 (securityContext.xml の中身)
FilterChainProxy
HttpSessionContext
IntegrationFilter
LogoutFilter
Authentication
ProcessingFilter
Exception
TranslationFilter
FilterSecurity
Interceptor
HttpSession から得られる情報をもとに
SecurityContextHolder を保存
url が “/j_acegi_logout” だったらユーザをログアウト
認証フォームの処理
フィルタ・チェイン内で投げられた例外の取扱
フィルタの実装を使って HTTP リソースのセキュ
リティを実現
AuthenticationProcessingFilter
Authentication
ProcessingFilter
“/j_acegi_security_check” が要求されたら
パラメタ “j_username”, “j_password” を
チェック
AuthenticationManager
DaoAuthenticationProvider UserDetailsService
AuthenticationProvider のリストの順に認証
要求を繰り返す
‣ daoAuthenticationProvider
‣ anonymousAuthenticationProvider
‣ rememberMeAuthenticationProvider
‣ JDBC, メモリ上, LDAP... など
FilterSecurityInterceptor
FilterSecurityInterceptor フィルタの実装を使って HTTP リソースの
セキュリティを実現
AuthenticationManager ( 非ログオン時)
RoleVoter
AuthenticatedVoter
AccessDecisionManager
FilterInvocationDefinitionSource
/login/**=IS_AUTHENTICATED_ANONYMOUSLY
/book/test/**=IS_AUTHENTICATED_FULLY
/book/**=ROLE_SUPERVISOR
/**=IS_AUTHENTICATED_ANONYMOUSLY
Request Map
( パーミッションのチェック)
ExceptionTranslationFilter
ExceptionTranslationFilter
フィルタ・チェイン内で投げられた例外の取扱
AuthenticationProcessingFilterEntryPoint
AccessDeniedHandler
ここにログイン・フォームの場所を保持
ユーザをログイン・フォームにリダイレクト
FORBIDDEN (403) を送るか, ユーザ
をエラー・ページにリダイレクト
その他の機能
シングル・サイン・オン (CAS)
LDAP サポート
X509 ( 証明書) サポート
HTTP ベーシック認証もサポート
HTTP ダイジェスト認証もサポート
もっとあるよ....
Acegi Security についてもっと知りたければ
Acegi Security のサイト
http://www.acegisecurity.org/
IBM Developerworks にいい記事有り
http://www.ibm.com/developerworks/java/
library/j-acegi1/index.html
「Acegi Security System for Spring」フォーラム
http://forum.springframework.org/
forumdisplay.php?f=33
Acegi on Grails
Acegi on Grails の使い方
Grails 上で必要なのは
DaoAuth で Grails のドメイン・クラスを使う
Grails アプリケーションで簡単に管理できる
GrailsDaoImpl と GrailsUser
RequestMap ドメイン・クラス
セキュアなページを動的に管理
Acegi on Grails をサポートするビーン
Taglibs と AuthService
セキュアな Ajax リクエスト (Web2.0 では必須)
Grails で Ajax 使うのは簡単. セキュアな Ajax も簡単
セキュアな Service メソッド
ドメイン・クラス
username : String password : String enabled : boolean
Person
authority : String Authority
0..*
1..*
- people - authorities
url : String
configAttribute : String Requestmap
• Person
ユーザ・アカウント
(User, Account, LoginUser)
• Authority
ユーザの権限
(Role, Group, Team)
• RequestMap
URL のセキュリティ用マップ
url:”/some/page/**”
configAttribute:
“ROLE_SUPERVISOR,ROLE_USER”
Acegi on Grails をサポートするビーン
GrailsWebApplicationObjectSupport
WebApplicationObjectSupport を extend した抽象クラス.
GrailsApplication を得るのに使う
GrailsWebApplicationObjectSupport
WebApplicationObjectSupport を extend した抽象クラス.
GrailsApplication を得るのに使う
org.springframework.web.context.support.WebApplicationObjectSupport
getGrailsApplication() : GrailsApplication setUpSession() : void
releaseSession() : void
GrailsWebApplicationObjectSupport
GrailsDaoImpl GrailsFilterInvocationDefinition
Acegi on Grails をサポートするビーン
GrailsWebApplicationObjectSupport
WebApplicationObjectSupport を extend した抽象クラス.
GrailsApplication を得るのに使う
GrailsDaoImpl
UserDetailsService を実装したクラス. User ドメイン・クラスを使う
のに必要
GrailsUser
org.acegisecurity.userdetails.User のサブクラス. User ドメイン・
クラスを SecurityContextHolder に出し入れするのに使う
GrailsDaoImpl と GrailsUser
<<interface>>
org.acegisecurity.userdetails.UserDetailsService
loadUserByUsername(String username) : UserDetails loginUserDomainClass : String
userName : String password : String enabled : String
relationalAuthorities : String authority : String
GrailsDaoImpl
GrailsWebApplicationObjectSupport
set Domain class
name and fields
name for
UserDetails
org.acegisecurity.userdetails.User
getDomainClass() : Object
setDomainClass(Object domainClass) : void domainClass : Object
GrailsUser
UserDetails ドメイン・クラスの 名前, そのフィールドの名前を設定
Acegi on Grails をサポートするビーン
GrailsWebApplicationObjectSupport
WebApplicationObjectSupport を extend した抽象クラス.
GrailsApplication を得るのに使う
GrailsDaoImpl
UserDetailsService を実装したクラス. User ドメイン・クラスを使うのに必
要
GrailsUser
org.acegisecurity.userdetails.User のサブクラス. User ドメイン・クラスを
SecurityContextHolder に出し入れするのに使う
GrailsFilterInvocationDefinition
FilterInvocationDefinitionSource を実装したクラス. RequestMap ドメイ
ン・クラスを使うのに必要
GrailsFilterInvocationDefinition
<<interface>>
org.acegisecurity.intercept.web.FilterInvocationDefinitionSource
lookupAttributes(String url) : ConfigAttributeDefinition requestMapClass : String
requestMapPathFieldMethod : String
requestMapConfigAttributeFieldMethod : String requestMapPathFieldName : String
GrailsFilterInvocationDefinition
GrailsWebApplicationObjectSupport
set Domain class
name and fields
name for
RequestMap
ReequestMap ドメイン・クラスの 名前, そのフィールドの名前を設定
securityContext
Grails Acegi プラグイン
を使えば簡単に統合可能
実装の概要
認証と認可はドメイン・クラス. 0.2 以降はテンプレートからコマンドで生
成
ログイン/ログアウトはコントローラ
セキュリティはタグ・ライブラリとサービス
Java クラス
UserDetailsService の実装クラスは Grails アプリケーションから
User ドメイン・クラスを使うために
FilterInvocationDefinitionSource の実装クラスは Grails アプリ
ケーションからリクエスト・マップを使うために
"doWithSpring" で Acegi のコンフィギュレーションを作成
パラメタは変更可能: AcegiConfig.groovy (0.2 以降)
Acegi プラグインの設定
プラグインのインストール
#grails install-plugin /path/to/grails-acegi-0.2.zip
#grails create-auth-domains (since 0.2)
テスト・データの設定 (BootStrap.groovy)
def admin = new Person(
username:"admin",
userRealName:"SystemAdmin",
passwd:"${DigestUtils.md5Hex("pass")}",
enabled:true,
email:"[email protected]")
admin.save()
def role = new Authority(authority:"ROLE_ADMIN")
role.addToPeople(admin)
role.save()
new Requestmap(url:"/book/create/**",configAttribute:"ROLE_ADMIN").save()
new Requestmap(url:"/book/list/**",
configAttribute:"IS_AUTHENTICATED_REMEMBERED").save()
リクエストマップ & Voter とは
IS_AUTHENTICATED_FULLY
not remember-me and anonymously
IS_AUTHENTICATED_REMEMBERED
remember-me or is fully authenticated.
IS_AUTHENTICATED_ANONYMOUSLY
remember-me, OR anonymously,
/login/**=IS_AUTHENTICATED_ANONYMOUSLY
/book/test/**=IS_AUTHENTICATED_FULLY
/book/**=ROLE_SUPERVISOR
/**=IS_AUTHENTICATED_ANONYMOUSLY
Request Map
AuthenticatedVoter
RoleVoter ROLE_ で始まる. 例: ROLE_SUPERVISOR,ROLE_USER
デモ
タグ・ライブラリとサービス
AuthorizeTagLib
ユーザ情報を得る
権限 (ROLE) に応じてユーザのビューを切り替える
AuthenticateService
おもに AuthorizeTagLib から呼び出される
authenticateService.principal() がプリンシパルを
返す
URL リクエスト・マップによるセキュリティは単純なケースではだいた
いOK. でもそれでは足りない場合もある. その場合には, コード中に何か
を仕込まないとダメ.
タグ・ライブラリ: if...Granted
<g:ifAllGranted
role="ROLE_ADMIN,ROLE_SUPERVISOR">
リストされたロールすべてを満たしていればOK
<g:ifAnyGranted
role="ROLE_ADMIN,ROLE_SUPERVISOR">
リストされたロールのどれかを満たしていればOK
<g:ifNotGranted
role="ROLE_USER">
リストされたロールに当てはまらなければOK
タグ・ライブラリ: User Info
loggedInUserInfo
<g:loggedInUserInfo
field="username"/>
isLoggedIn
<g:isLoggedIn>
ログインしたユーザ向けの内容
</g:isLoggedIn>
isNotLoggedIn
<g:isNotLoggedIn>
匿名ユーザ向けの内容
</g:isNotLoggedIn>
class Person {
String username
String password
String userRealName
String email
}
ドメイン・クラスか
ら値を取ってくる
認証サービス
おもに AuthorizeTagLib から呼び出される
コントローラで有用
authenticateService.principal()
• principal は org.acegisecurity.userdetails.User クラス
例えば
def principal = authenticateService.principal()
println principal.getUsername()
println principal.getAuthorities()
authenticateService.userDomain()
• 認証されたユーザのドメイン・クラス
例えば
println "userDomain() ${authenticateService.userDomain()}"
Ajax なリンクのあるページ
Ajax な出力
Ajax をセキュアにする
でもログイン・ページが箱の中じゃ, かっこ悪い!!
ログインページ
セッションの時間切れ後にリ
ンクをクリックすると...
Ajax をセキュアにする
Ajax な出力
Ajax なページ こうなるといい
ログイン・ダイアログ
がポップアップ
Ajax をセキュアにする
Ajax スタイルのリスポンスが必要 ( ログイン・ページで
ではなく)
JSON , XML , text , html parts
ほとんどの Ajax フレームワーク
(prototype.js, YUI など) は
“X-Requested-With” ヘッダを送信
もし自分が使っている Ajax フレームワークがそうでなければ, そ
のように改造する
Ajax をセキュアにする
non AJAX request Normal Login form
request
response
ExceptionTranslationFilter
FilterSecurity
Interceptor
Exception
Login error Access Denied