Play2 + pac4jで簡単にOAuth認可をする
はじめに
最近のWebアプリケーションは、やれTwitter連携だやれFacebook連携だとOAuthを利用した認可機能を盛り込むことが多いと思います。しかし、連携するサービスによってAPIの呼び出し方が異なっていたりと、自分で実装をするとコードのフラグメント化が激しいことになります。
pac4jは、外部サービスアカウントの認証、認可に関する処理の共通化を行う、Javaのライブラリです。詳細は公式サイトを見るのが早いのですが、
-
OAuth1.0,2.0
- DropBox
- Github
- Twitter...
-
CAS
-
HTTP
- form
- basic
-
OpenID
-
SAML2.0
と、幅広いProtocol、サービスに対応をしています。また、いくつかのフレームワーク向けにプラグイン、ラッパーも提供されていて組み込みも簡単です。今回は、Play2.2xとScalaを利用して、Twitterの認可を行います。
Twitterアプリを登録する
http://dev.twitter.com/からアプリの登録を行って、Api Key
,Api Secret
を取得しておきます。また、コールバックURLの指定は${FQDN}/callback
とします。
依存関係を追加する
build.sbt
に依存関係を追加します。
libraryDependencies += "org.twitter4j" % "twitter4j-core" % "4.0.1"
resolvers ++= Seq(
"Sonatype snapshots repository" at "https://oss.sonatype.org/content/repositories/snapshots/"
)
libraryDependencies ++= Seq(
"org.pac4j" % "play-pac4j_scala" % "1.2.0",
"org.pac4j" % "pac4j-oauth" % "1.5.1"
)
今回、TwitterAPIを利用するために、Twitter4Jを利用しています。
設定ファイルを作る
Playのconf
ディレクトリにpac4j.conf
ファイルを以下の内容で作ります。
{
"twitter": {
"api": {
"key": "先ほど取得したapi key",
"secret": "先ほど取得したsecret",
"callback": "Twitterアプリ作成時に設定したコールバックのURL"
}
}
}
なお、このファイルはアプリ固有情報を含みます。バージョン管理システムなどを利用する際には、適切に設定を行って管理をしてください。
設定ファイルから読み込む部分を作る
次のようなtrait
を用意します。
package models
import play.api.Configuration
import com.typesafe.config.ConfigFactory
import java.io.File
import models.TwitterAuthConfig.ConfigurationError
trait TwitterAuthConfig {
lazy val consumer : TwitterConsumer = {
val config = Configuration(ConfigFactory.parseFileAnySyntax(new File("conf/pac4j.conf")))
(for {
key <- config.getString("twitter.api.key")
secret <- config.getString("twitter.api.secret")
} yield {
TwitterConsumer(key, secret)
}).getOrElse(throw new ConfigurationError("conf/pac4j.conf is not define or format is invalid"))
}
lazy val callback = {
val config = Configuration(ConfigFactory.parseFileAnySyntax(new File("conf/pac4j.conf")))
(for {
callback <- config.getString("twitter.api.callback")
} yield {
callback
}).getOrElse(throw new ConfigurationError("conf/pac4j.conf is not define or format is invalid"))
}
}
object TwitterAuthConfig {
class ConfigurationError(m: String) extends RuntimeException(m)
}
case class TwitterConsumer(key: String, secret: String)
アプリのエントリーポイントを設定する
Playのアプリが立ち上がった際に呼び出されるコールバックをOverrideします。Global.scala
ファイルを作り、以下の内容とします。
package controllers
import play.api.GlobalSettings
import play.api.mvc.{ Results, SimpleResult, RequestHeader }
import scala.concurrent.Future
import org.pac4j.oauth.client.TwitterClient
import org.pac4j.core.client.Clients
import org.pac4j.play.Config
import models.twitter.TwitterAuthConfig
object Global extends GlobalSettings with TwitterAuthConfig {
override def onError(request: RequestHeader, ex: Throwable): Future[SimpleResult] = {
Future.successful(Results.InternalServerError)
}
override def onStart(app: play.api.Application): Unit = {
val ti = consumer
val tc = new TwitterClient(ti.key, ti.secret)
val cl = new Clients(callback, tc)
Config.setClients(cl)
}
}
また、このクラスが呼び出されるように、application.conf
に書き足します。
application.global=controllers.Global
ルーティングを設定する
routes
に以下の内容を追記します。
# Authorication
GET /callback org.pac4j.play.CallbackController.callback()
POST /callback org.pac4j.play.CallbackController.callback()
GET /casProxyCallback org.pac4j.play.CallbackController.callback()
POST /casProxyCallback org.pac4j.play.CallbackController.callback()
GET /logout org.pac4j.play.CallbackController.logoutAndRedirect()
コントローラーを実装する
最期にコントローラーの実装を行います。今回は、
- Twitterの認可がされてないなら、認可URLを表示
- Twitterの認可があるなら、上位のツイートを表示
するコントローラーを実装します。
Application..scala
を以下の内容とします。
package controllers
import play.api.mvc._
import org.pac4j.play.scala.ScalaController
import org.pac4j.oauth.profile.twitter.TwitterProfile
import models.TwitterAuthConfig
import twitter4j.auth.AccessToken
import twitter4j.TwitterFactory
import scala.collection.JavaConversions._
object Application extends ScalaController with TwitterAuthConfig {
lazy val twitter = {
val tw = TwitterFactory.getSingleton
tw.setOAuthConsumer(consumer.key, consumer.secret)
tw
}
def index = Action { request =>
val newSession = getOrCreateSessionId(request)
val content = Option(getUserProfile(request)).fold(getRedirectAction(request, newSession, "TwitterClient", "/").getLocation) { p =>
val tp = p.asInstanceOf[TwitterProfile]
val ac = new AccessToken(tp.getAccessToken, tp.getAccessSecret)
twitter.setOAuthAccessToken(ac)
twitter.getUserTimeline.map(_.getText).mkString("\n")
}
Ok(content)
}
}
動かす
とりあえず/
にアクセスをすると認可用のURLが表示されるので、そのページへアクセスします。Twitterの認可を了承すれば、画面に最近のツイートが表示されるはずです。
以上です。お疲れ様でした。
おわりに
今回の実装はあくまでサンプルなので、エラー処理とかが曖昧です。例えば、ユーザによって認可が拒否された場合の処理とかがまだ微妙です。また、pac4jやplayframeworkのアップデートは非常に早いです。自分が利用しているバージョンに注意を払い、公式サイトやリポジトリのドキュメントを参照しましょう。