Grails 3.2.8と3.2.9でSpring Security CoreとDatabase Migrationを利用する際の注意点
前提条件
Spring Security Coreラグインのバージョンは3.1.2
です。
Database Migrationプラグインのバージョンは3.0.0
です。
また、認証用のドメインを作成するs2-quickstart
コマンドを以下のように実行したと仮定します。
s2-quickstart example User Role
これで、User
とRole
、UserRole
というドメインが生成されます。
Grails 3.2.8の場合
Spring Security Coreプラグイン
デフォルトでは正しく動きません
というのも、Grails3.2.8からドメイン内のプロパティに対してDIがデフォルトで動かないようになったためです。
これはパフォーマンスのための措置とのことです。
通常のアプリケーションであれば特に問題になることはないと思いますが、Spring Security Coreラグインを利用している場合、User
ドメイン(User.groovy
)にSpringSecurityService springSecurityService
というプロパティが設定されています。
今までは、User
ドメインをsave
する際にUser#encodePassword()
が呼ばれて、その中でspringSecurityService
を使って自動的にパスワードを暗号化してくれていました。
しかしGrails 3.2.8からはspringSecurityService
がDIされていないためnullになっており、それが原因でパスワードは暗号化されずに平文のままDBに保存される状態になっています。
ログインページからログインしようとした場合、入力したパスワードは自体は本来の暗号化アルゴリズムで暗号化されてDBに問い合わせされるので、当然一致するパスワードが無いのでログイエラーとなってしまいます。
この問題の解決するためには、GORMのDIを以前と同じように有効化する必要が有ります。
具体的には、application.yml
で、autowire
をtrue
にするだけです。
grails: gorm: autowire: true #これをfalseからtrueへ。もしなければ単に追加すればOK。
Grailsを3.2.7から3.2.8にアップグレードした場合、既存ユーザのログイン自体は問題なく動作して、この問題は新しいユーザを追加したタイミングで初めて発生します。
非常に気付きづらいので、Spring Security Coreプラグインを利用している場合、3.2.8にアップグレードするのであればこの部分は要注意です。
Database Migrationプラグイン
さらに、データベースがH2(デフォルト)で、Database Migrationプラグインを利用する際にも注意が必要です。
問題は単純で、User
ドメインのpassword
というカラム名がDatabase Migrationプラグインからうまく利用できません。
起動時にマイグレーションツールを適用(updateOnStart: true
)しようとすると、以下のようなエラーが発生します。
grails> run-app | Running application... Configuring Spring Security Core ... ... finished configuring Spring Security Core INFO 17/05/24 13:14: liquibase: Can not use class org.grails.plugins.databasemigration.liquibase.GormDatabase as a Liquibase service because it does not have a no-argument constructor 2017-05-24 13:14:23.099 ERROR --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : 列 "THIS_.password" が見つかりません Column "THIS_.password" not found; SQL statement: select this_.id as id1_8_0_, this_.version as version2_8_0_, this_.account_expired as account_3_8_0_, this_.account_locked as account_4_8_0_, this_.date_created as date_cre5_8_0_, this_.enabled as enabled6_8_0_, this_.last_updated as last_upd7_8_0_, this_."password" as pa ssword8_8_0_, this_.password_expired as password9_8_0_, this_.username as usernam10_8_0_ from user this_ where this_.username=? [42122-193] 2017-05-24 13:14:23.184 ERROR --- [ main] o.s.boot.SpringApplication : Application startup failed
解決策として、User.groovy
に自動生成されるmappingの部分でDBのカラム名を以下のように変更します。
static mapping = { // これは元々あったやつ。コレがマズイ。(H2) //password column: '`password`' //コレを追加 password column: 'passwd' }
これで、再度dbm-gorm-diff
でGORMから差分適用用のchangelogを生成しなおせばちゃんとマイグレーションが動作します。
Grails 3.2.9の場合
Spring Security Coreプラグイン
Spring Security Coreプラグインを利用すること自体には特に問題はありません。
Database Migrationプラグイン
もしもDatabase Migrationプラグインも利用するなら要注意です。
というよりも、結論としてはDatabase MigrationプラグインとSpring Security Coreラグインを同時に利用するのであれば、Grails 3.2.9は見送ったほうが良いと思います。
Hibernate5との絡みの問題らしくて、既にフィックスしたという情報も有りますが少なくとも私の環境では動作しませんでした。
これはGrails 3.2.9がデフォルトで利用するGORMのバージョンが6.0.10.RELEASE
になったために発生します。
このGORMのバージョンからs2-quickstart
コマンドで生成するUser.goorvy
には、パスワードの暗号化用のメソッドencodePassword()
が記載されなくなっています。
その代わりに、新しいsrc/groovy/example/UserPasswordEncoderListener.groovy
というファイルが生成されます。このファイルの中に暗号化用の処理が移動されたようです。
そして、resources.groovy
にそのUserPasswordEncoderListener
のインスタンスを生成する以下のコードが1行挿入されます。
userPasswordEncoderListener(UserPasswordEncoderListener, ref('hibernateDatastore'))
冒頭にも述べましたが、Spring Security Coreプラグイン自体は正しく動作するので、特に問題ありません。
また、最初のGrails 3.2.8のDI問題を解決するためにこの仕組みが導入されたわけなのでSpring Security Coreプラグインとしては正当な進化となります。
しかし、残念ながらDatabase Migrationプラグインと同時に利用することが出来ません。
この状態でDatabase Migrationプラグインのコマンド(dbm-*
)を実行すると、以下のようなエラーが発生します。
*************************** APPLICATION FAILED TO START *************************** Description: The dependencies of some of the beans in the application context form a cycle: authenticationUserDetailsService ↓ userDetailsService ┌─────┐ | hibernateDatastore ↑ ↓ | userPasswordEncoderListener └─────┘
resources.groovy
に追加したコードからインスタンスの参照なりが循環してしまってエラーになっているようです。
なので、もしもresources.groovy
から件の1行のコードを省けばパット見動作はするようになりますが、既に述べたように本来それは平文のパスワードを暗号化するために利用されるものなので、結果的にGrails 3.2.8の時のように平文でパスワードが保存されるようになり、ログインできなくなります。
こちらに関しては、Grails 3.3以降、対応したSpring Security Coreプラグイン(3.2.0系になる予定)などがリリースされるはずですので、そのタイミングで再度確認したほうがいいかなと思います。
まとめ
今回詳細は述べませんでしたが、どうやらGrails 3.2.9だとAsset-Pipelineを利用するとwarに固めでデプロイするとAssetアクセスできないという問題も有るようです。
なので、個人的にはGrails 3.2.9は使わずに、Grails 3.2.8で、application.yml
のgrails.gorm.autowire
をtrue
にして様子見をするのが良いと思います。
参考
Grails Spring Security Core Plugin
Grails Database Migration Plugin
Error creating ‘hibernateDatastore’ - circular reference when running plugin commands
runCommand detects a dependency cycle for AbstractPersistenceEventListener Bean
23. Tutorials
Grails 3.2.8 Upgrade Notes.md