社内コミュニティで作っている とあるアプリ をPlay2.2からPlay2.3にマイグレーションしました。 Playのマイグレーションと言うよりは、SecureSocialのバージョンアップで苦労したので、その記録を残しておきます。
ご利用上の注意事項
網羅した情報ではありません。私が使っているせまーい範囲での情報です。
- 利用している認証方式はUserPasswordのみです。OAuthの情報はありません。
- この時点でいらない子のような気もしますが、まぁ整理するための練習と思って書きます。
- Viewに関する情報はありません。
- SecureSocialはフロントから呼び出す認証APIを実装するために利用しています。
お品書き
- どのバージョンを使うか?
- 参考になるコード
- 変わってたところ
- Scalaで書くしかなさそうなとこ
- 実装で悩んだところ
- build.sbt
- グローバル設定
- UserService
- コントローラー
- Future
- テスト (Cookieどうするの?)
どのバージョンを使うか?
sonatype-snapshots/ws/securesocial/securesocial_2.11/master-SNAPSHOT を使えばよいようです。
参考になるコード
SecureSocialのリポジトリにあるサンプルアプリが参考になります。
securesocial/samples/java/demo at master · jaliss/securesocial
変わってたとこ
- プラグインの管理方法
- Identityクラス
- UserServiceのインターフェース
- ctx().args.get(SecureSocial.USER_KEY)が返すオブジェクト
プラグインの管理方法
従来のバージョンではplay.pluginsで管理していました。 master-SNAPSHOTでは、securesocial/RuntimeEnvironment クラスで利用するプラグインを管理します。
RuntimeEnvironment.Defaultクラスを継承すると、楽に作れます。
注意点
グローバル設定を行うクラスで、getControllerInstanceをオーバーライドする必要があります。
参考になるコード
securesocial/MyEnvironment.scala です。 ※ このクラスはScalaで書くしかないようです。
Identityクラス
Identityクラスがなくなりました。代わりにBasicProfileクラスを使います。
UserServiceのインターフェース
- 戻り値にF.Promiseを使うようになった
- doSaveに引数SaveModeが追加された
- doSave(Token) -> doSaveToken(Token)に変更された
- 新しいメソッドが追加された
- doLink
- doPasswordInfoFor
- doUpdatePasswordInfo
実装はサンプルアプリを参考にするとよいです。
ctx().args.get(SecureSocial.USER_KEY)が返すオブジェクト
従来は、Identityクラスのオブジェクトでした。
master-SNAPSHOTでは、開発者が指定したクラスのオブジェクトになりました。地味に便利。
Scalaで書くしかなさそうなとこ
Javaで書く方法が分からなかったとこです。
難しいことをしなかったので、サンプルアプリのコピペでほぼ対応できます。
- ViewTemplatesを継承したクラス
- RuntimeEnvironmentを継承したクラス
実装で悩んだところ
build.sbt
"ws.securesocial" %% "securesocial" % "master-SNAPSHOT"
でいいんだ。と分かるまでに一苦労しました。 ググると、以下のようなコードを見つけることができますが、2015年4月時点ではここまでしなくてもよいようです。
lazy val root = (project in file(".")).enablePlugins(PlayScala)
.dependsOn(ProjectRef(uri("https://github.com/ewiner/securesocial.git#play-2.3"), "mainModule"))
もう1点、SNAPSHOTを参照する場合は、
resolvers += Resolver.sonatypeRepo("snapshots")
を付けるのを忘れないようにしましょう。
グローバル設定
サンプルコードにあるgetControllerInstanceがいるのかどうかに悩みました。
結論、必要です。
private RuntimeEnvironment env = new MyEnvironment();
@Override
public <A> A getControllerInstance(Class<A> controllerClass) throws Exception {
A result;
try {
result = controllerClass.getDeclaredConstructor(RuntimeEnvironment.class).newInstance(env);
} catch (NoSuchMethodException e) {
// the controller does not receive a RuntimeEnvironment, delegate creation to base class.
result = super.getControllerInstance(controllerClass);
}
return result;
}
UserService
何を実装すればいいのかよく分からなかったものがあります。
- SaveModeのLoggedInでやること
- doLink, doPasswordInfoFor, doUpdatePasswordInfo
- doLinkはサンプルアプリに実装例がある
- doPasswordInfoFor, doUpdatePasswordInfoは
が、空実装でもマイグレーション前と同等の機能は提供できているので、気にしないことにしました(オイオイ)。
コントローラー
サンプルコード securesocial/Application.java を見ると、
のような実装になっています。
いずれもプラグインを管理するRuntimeEnvironmentを参照するためなのかな? と思います。
ただ、コントローラーでSecuredActionまたは、UserAwareActionアノテーションを使っていれば、securesocial.core.java.SecureSocialのevnメソッドを使ってRuntimeEnvironmentが参照できるようなので、上記の対応はしませんでした。
以下、調べてみたことです。
- securesocial.core.java.SecureSocialクラスのenvメソッドでは Http.Context.current().argsのキー"securesocial-env"に入っているオブジェクトを返す
- Http.Context.current().argsにオブジェクトをセットするのは SecuredクラスのinitEnvメソッド
- SecuredクラスのinitEnvは以下のアノテーションで使われている
- SecuredAction
- UserAwareAction (で使われているUserAwareクラス)
- SecuredActionまたは、UserAwareActionアノテーションを使っていれば、SecuredクラスのinitEnvが呼び出されるので、securesocial.core.java.SecureSocialクラスのenvメソッドを使ってRuntimeEnvironmentを呼び出すことができる
Future
SecureSocialだからと言うわけではないのですが、絡みで苦労したので。
Futureでラッピングされたオブジェクトをどう参照すればいいか? で悩みました。
例えばこんなの。
Future<Option<Authenticator<LocalUser>>> futureAuthenticator ...
futureAuthenticator.value().get().get() で Option<Authenticator
concurrency - Scala - futures does not run - Stack Overflow を見ると、待ちが必要らしいです。
で、こんな感じでまってみました。が、これでいいのか良く分かっておりません…orz
Await.result(futureUser, Duration.create(10, TimeUnit.MILLISECONDS));
テスト (Cookieどうするの?)
コントローラーのテストをするのに、リクエストにCookieを付けたい場面があります。 このCookieをどう作るの? と言う話です。
従来は java - Unit-testing methods secured with Securesocial annotation - Stack Overflow を参考に実装していました。
master-SNAPSHOTでは、Cookieの作成に必要なプラグインをRuntimeEnvironmentから参照する必要があります。 なので、実装方法を変更しなければいけません。
残念ながらその方法が良く分からなかったので、playframework 2.3 - Unit testing securesocial authentication for play2.3.x controllers in scala - Stack Overflow を参考に、実際にAPIを呼び出し、受け取ったCookieを使うことにしました。強引ではありますが…。
Scalaな方は、この辺 securesocial/WithLoggedUser.scala を利用にすると幸せになれそうです。
最新のバージョンには対応していない(Identityクラスを使っている)ようなので、修正が必要と思います。