spring-security-core 3.0.0.M1 https://bintray.com/grails/plugins/org.grails.plugins%3Aspring-security-core/view
compile ":spring-security-core:3.0.0.M1"
Grails 3.0.8 + spring-security-core 3.0.0.M1 でプロダクトコード作ってて、spring-security-coreのデフォルトハッシュアルゴリズムがSHA256からBCrypt(Blowfishとも)に変わったことで気をつけないとハマりそうなのでここに残します。
みなさんもう既にご存知かとは思いますが、spring-security-coreプラグインはGrailsで作ったアプリに入れるだけでログイン画面やユーザ/ロール管理やアクセス制御が簡単にできちゃう超イケてるプラグインです。Grailsは3.0からSpringBootベースになったので、それに対応してspring-security-coreプラグインも3.0化されてます。今日はそれの話。
SHAファミリではソルトとストレッチングを自力で面倒見る必要がありましたが、BCryptはそれ自体がソルトとストレッチングの仕組みを持っているので、デフォルトでBCryptを使うこと自体は大変喜ばしいことです。
が、
grails.plugin.springsecurity.password.algorithm = 'bcrypt'
のときにSHAファミリと同じ気分で
springSecurityService.encodePassword("mypassword") == springSecurityService.encodePassword("mypassword")
を試しても成り立たない。ウェッ!?ってなる人いそうですが、アルゴリズム自体がパスワードエンコードの際に乱数でソルトを生成して毎回異なるハッシュを生成するものですので、当たり前っちゃ当たり前ですね。
で、SpringSecurityの org.springframework.security.crypto.password.PasswordEncoder インタフェースに matches()
メソッドがある!これ使えばいいやん!と思うと…
springSecurityService.passwordEncoder.matches("mypassword",userDomain.password)
そんな メソッドはねえ! って怒られますね。マジか。どうしたら。
A. isPasswordValid()
メソッドを使います。
springSecurityService.passwordEncoder.isPasswordValid(userDomain.password,"mypassword",null)
第三引数はソルトを指定するものですが、BCryptではソルトがハッシュに埋め込まれているので、常時nullでOKです。
spring-security-coreプラグインのドキュメントを見ても、 isPasswordValid()
を使ってくれ的な解説は無いんですが、 passwordEncoder
の実体が CryptoEncoderWrapper
なので、 isPasswordValid()
メソッドを使う形になってるんですよね。そしてSpringSecurityにも org.springframework.security.authentication.encoding.PasswordEncoder インタフェースの方は isPasswordValid()
メソッドで実装されてた。
SpringSecurityのつくりそのまま、だからプラグインの方にはドキュメントに書かれてないんでしょうね。