Grails AuthenticationとEmailConfirmationプラグインでRailsのDeviseと同じ機能を実装する
Javaの実行環境で、Ruby on RailsのプラグインDeviseのようにメール認証付きのログイン機構を簡単に実装できないかなあと思い調べてみました。
まず、Java実行環境で動くRoRスタイルのフレームワークは、最近のメジャーどころは以下のようなところかと思います。
- JRuby on Rails: JRubyのRoR
- Play! framework: scalaに対応したので最近人気
- Grails: Javaで動くLL では老舗のGroovyで開発するRoRスタイルのフレームワーク
上記3つを簡単に調べた印象ですが、まずJRubyは最新版ではDeviseがすんなり動いてくれませんでした。
次に、Play! frameworkですが、軽くしか調べてないですがメールのコンファームまでできるプラグインが見つかりませんでした。まだプラグインなどは充実していない印象を受けたので今回は調査を見送り。
そこで、昔から在るGroovyのRailsであるGrailsを見てみたら目的のプラグインが見つかったので、Grailsのインストールからメールのコンファームを組み込んだ認証機構を実装する手順をメモしておきます。
Grailsとは
Grailsは最初のバージョンが2006年にリリースされたRoR Likeなフレームワークで、開発言語はJavaで動くスクリプト言語の元祖であるGroovyを使います。GはGroovyのGです。
今はSpringSourceのプロジェクトにもなり、VMWareがバックに居るため将来のメンテナンスについても安心して使うことができそうです。
マニュルもかなり充実しています。
http://grails.org/doc/latest/guide/
Grailsのインストール
インストールは、http://grails.org/Installationに書いてある通り、解凍してパスを通すだけです。
以下、Mac OS X 10.7.2の場合の手順をメモしておきます。
http://grails.org/Downloadからgrails-2.0.0.zipをダウンロード
解凍して、binをパスに通し、解凍したディレクトリをGRAILS_HOMEとして環境変数に設定する。
また、JAVA_HOMEも必要です。
grailsは/optの下に解凍したので、以下のように.bash_profileの設定しました。
JAVA_HOME=/usr/bin/java GRAILS_HOME=/opt/grails-2.0.0 PATH=$GRAILS_HOME/bin:$PATH
設定を適用
$ source .bash_profile
アプリケーションの生成と動作確認
適当なworkspaceに移動して、以下のコマンドをたたきます。(rails newに当たります)
$ grails create-app my-project
jarのダウンロードなど少々時間がかかります。
$ cd my-project $ grails run-app
Downloading: resources-1.1.5.zip |
run-appするとVMWareに情報を送っていいか?と聞かれますが、問題無さそうなのでyを選択。
ブラウザでlocalhost:8080/my-projectを表示すると以下のようなデフォルトのページが表示されます。
scaffold
ドメインモデルの生成
$ grails create-domain-class org.example.Book Created file grails-app/domain/org/example/Book.groovy Created file test/unit/org/example/BookTests.groovy
Book.groovyを以下のように編集します
package org.example class Book { String title String author static constraints = { title(blank: false) author(blank: false) } }
コントローラーの生成
$ grails create-controller org.example.Book Created file grails-app/controllers/org/example/BookController.groovy Created file grails-app/views/book Created file test/unit/org/example/BookControllerTests.groovy
package org.example class BookController { //def index {} def scaffold = Book // Note the capital "B" }
$ grails run-app
Authenticationプラグインで認証を組み込む
実装するサインナップからの画面遷移を確認しておきます。
- サインナップが面を表示
- 登録後、確認メールを送信
- メールのリンクをクリック
- 確認後指定されたアクションに遷移
まず、基本的な認証機構を提供するAuthenticationプラグイン(http://grails.org/plugin/authentication)をインストールします。
$ grails install-plugin authentication
フィルターを設定
Grailsのフィルター(サーブレットフィルター)は以下に解説があります。
http://grails.org/doc/latest/guide/theWebLayer.html#6.6 Filters
プラグインのドキュメントの通り、以下のようにフィルターを設定します。
$ vi grails-app/conf/AuthenticationFilters.groovy
class AuthenticationFilters { static nonAuthenticatedActions = [ [controller:'authentication', action:'*'] ] def filters = { accessFilter(controller:'*', action:'*') { before = { boolean needsAuth = !nonAuthenticatedActions.find { (it.controller == controllerName) && *1 } if (needsAuth) { return applicationContext.authenticationService.filterRequest( request, response, "${request.contextPath}/authentication/index" ) } else return true } } } }
次に、サインナップ後確認メールを送信する仕組みを組み込んで行きます。
メール確認の仕組みはEmail Confirmationプラグイン(http://grails.org/plugin/email-confirmation)を利用します。
Email Confirmationはmailプラグインとquartzプラグインに依存しているので、それらもインストールします。
$ grails install-plugin mail $ grails install-plugin quartz $ grails install-plugin email-confirmation
メール送信設定を、http://grails.org/plugin/mailのConfigurationを参考にConfig.groovyに追加します
$ vi grails-app/Config.groovy
ファイルの末尾に以下を追加
grails { mail { host = "smtp.gmail.com" port = 465 username = "youracount@gmail.com" password = "yourpassword" props = ["mail.smtp.auth":"true", "mail.smtp.socketFactory.port":"465", "mail.smtp.socketFactory.class":"javax.net.ssl.SSLSocketFactory", "mail.smtp.socketFactory.fallback":"false"] } }
Email Confirmationのコントローラーも認証から除外するようにフィルターを設定します。
class AuthenticationFilters {
static nonAuthenticatedActions = [
[controller:'authentication', action:'*'],
[controller:'emailConfirmation', action:'*']
]
...
}
次に、AuthenticationプラグインのonSignupイベントを使って、サインアップ後に入力されたメールアドレスに対して確認メールを送信する設定をしていきます。
$ vi grails-app/conf/BootStrap.groovy
import org.springframework.web.context.support.WebApplicationContextUtils class BootStrap { def emailConfirmationService def init = { servletContext -> def appCtx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext) // Emailによる確認を有効にする(1) appCtx.authenticationService.events.onConfirmAccount= { user -> return true } // サインナップ後に呼ばれるトリガー(2) appCtx.authenticationService.events.onSignup = { user -> // user.paramsにはサインナップでポストされたパラメータが含まる // sendConfirmationの第4引数は、以下のonConfirmationのuidに渡る(3) emailConfirmationService.sendConfirmation(user.params.email, "Please confirm", [from:"hrendoh@gmail.com"], user.params.login) } // 確認メールのリンクをクリックして、トークンを確認した後呼ばれる emailConfirmationService.onConfirmation = { email, uid -> log.info("User with id $uid has confirmed their email address $email") appCtx.authenticationService.confirmUser(uid) return [controller:'book', action:'index'] } emailConfirmationService.onInvalid = { uid -> log.warn("User with id $uid failed to confirm email address after 30 days") } emailConfirmationService.onTimeout = { email, uid -> log.warn("User with id $uid failed to confirm email address after 30 days") } } def destroy = { } }
各プラグインのマニュアルからポイントのみ抜粋します。
(1) Emailによる確認を有効にする
Called to see if email confirmation is required, return true if user cannot log in yet until confirmed
onConfirmAccount: { user -> }
(http://grails.org/plugin/authentication)
(2) サインナップ後に呼ばれるトリガー
Called on successful signup, although email may not be confirmed yet - params are the request (form) params
onSignup: { params -> }
(http://grails.org/plugin/authentication)
(3) sendConfirmationにuidを渡す
def sendConfirmation(String emailAddress, String theSubject, Map model = null, String userToken = null)
userToken - a string that your application can use to tie up this request to your own data. This is passed back to your application in onConfirmation and onTimeout events, along with the email address.
(http://grails.org/plugin/email-confirmation)
以上で、独自のユーザアカウントで認証をするWebアプリのひな形が出来上がります。
後は、以下のような項目が必要となりますが、今回はここまで
- パスワードリセット
- ログインIDをDeviseと同じようにEmailにする
- Spring Securityなどとの連携
これだけで、RailsのDeviseと同じ機能をほぼ実装できているので、実行環境がJavaじゃないとだめな場合にはかなり重宝しそうな気がします。
*1:it.action == '*') || (it.action == actionName