2014年12月31日水曜日

Yesod AuthのHashDB認証をやってみた

YesodのHashDBプラグインを利用した認証処理をやってみた

いま、最も熱いWeb FrameworkのYesod...って言いたいけど、最近、西のほうのPython遣いたちに追いやられている感はあるものの、でもやっぱYesodは最高ということで、

Yesodの認証処理を調べていて分かったことを忘れないうちにまとめてみました



Yesodの認証処理


Yesodの認証処理には複数のプラグインがありましてyesod-authというパッケージにまとめられています、主なプラグインは

  • OpenID認証
    • 試していません Orz...
     
  • BrowserID認証
    • 試していません Orz...
     
  • email認証
    • メールアドレスでアカウント発行、ログインまでを実装してあるプラグイン、アカウント新規発行時は確認メールを送信するため、サーバーでpostfixなんかのMTAを動かさなければならないのでチョット面倒、外部のメールサーバーにも出来る
     
  • Hash認証
    • 通常のアカウントとパスワードによる認証機構、仕組みを理解するのに簡単なので今回はこちらでお勉強 と言う感じで外部の認証機構をそのまま利用できる今時のプラグインが実装されている。 
 このうちHash認証でサンプルを構築してみました。

認証のサンプルを作成


とりあえずScaffoldサイトを構築する、MySQLを利用するのでその辺も起動しておく


Scaffoldの作成

cuomo@karky7 ~/Code $ yesod init
Welcome to the Yesod scaffolder.
I'm going to be creating a skeleton Yesod project for you.

What do you want to call your project? We'll use this for the cabal name.

Project name: YesodAuthSample
Yesod uses Persistent for its (you guessed it) persistence layer.
This tool will build in either SQLite or PostgreSQL or MongoDB support for you.
We recommend starting with SQLite: it has no dependencies.

    s      = sqlite
    p      = postgresql
    pf     = postgresql + Fay (experimental)
    mongo  = mongodb
    mysql  = MySQL
    simple = no database, no auth
    url    = Let me specify URL containing a site (advanced)

So, what'll it be? mysql
That's it! I'm creating your files now...
...
...

HashDB認証を構築


まず必要なパッケージをインストール
karky7 ~ # emerge -pv dev-haskell/yesod-auth-hashdb

These are the packages that would be merged, in order:

Calculating dependencies... done!
[ebuild   R   ~] dev-haskell/yesod-auth-hashdb-1.4.1.1:0/1.4.1.1::karky7  USE="doc hscolour profile {-test}" 0 KiB

Total: 1 packages (1 reinstalls), Size of downloads: 0 KiB

 * IMPORTANT: 27 news items need reading for repository 'gentoo'.
 * Use eselect news to read news items.

karky7 ~ #
上のパッケージはgentoo-haskellへmergeしていただいたのでそちらでもインストール出きるはずです

データベースへの接続設定をする


Databaseへ接続するためsettings.ymlを修正
cuomo@karky7 ~/Code $ cd YesodAuthSample
~/Code/YesodAuthSample $ git diff
diff --git a/config/settings.yml b/config/settings.yml
index 4145c02..62a7dd4 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -16,9 +16,9 @@ ip-from-header: "_env:IP_FROM_HEADER:false"
 
 database:
   user:     "_env:MYSQL_USER:YesodAuthSample"
-  password: "_env:MYSQL_PASSWORD:YesodAuthSample"
+  password: "_env:MYSQL_PASSWORD:abc12345"
   host:     "_env:MYSQL_HOST:localhost"
-  port:     "_env:MYSQL_PORT:5432"
+  port:     "_env:MYSQL_PORT:3306"
   database: "_env:MYSQL_DATABASE:YesodAuthSample"
   poolsize: "_env:MYSQL_POOLSIZE:10"

~/Code/YesodAuthSample $ 

modelの作成


ログインアカウントを管理するUser型を作成、他のモデルは削除しても構いません
User
    email Text
    password Text
    UniqueUser email
    deriving Typeable

 -- By default this file is used in Model.hs (which is imported by Foundation.hs)

ルーティングを若干修正


HomeRのPOSTを利用しないので削除

/static StaticR Static appStatic
/auth   AuthR   Auth   getAuth

/favicon.ico FaviconR GET
/robots.txt RobotsR GET

/ HomeR GET

YesodAuthインスタンスを修正


ディフォルトだとBrowserIDの認証設定になっているので、ここをHashDBプラグインへ修正する、User型をHashDBUserクラスのインスタンスへすることも忘れずに。renderAuthMessageはログイン後に出力されるメッセージをカスタマイズするためにloginMessage関数へ差し替えています。詳しくはYesod.Auth.Messageモジュールを参照してみてください(japaneseMessageとかもあるよ)

instance YesodAuth App where
    type AuthId App = UserId

    -- ログイン後のリダイレクト先
    loginDest _ = HomeR
    -- ログアウト後のリダイレクト先
    logoutDest _ = HomeR
    -- Override the above two destinations when a Referer: header is present
    redirectToReferer _ = True

    -- UserIdを取得するための関数を設定、maybeAuthId系の関数が利用可能になる
    getAuthId creds = getAuthIdHashDB AuthR (Just . UniqueUser) creds

    -- プラグインをauthHashDBへ修正
    authPlugins _ = [authHashDB (Just . UniqueUser)]

    -- ログイン後のメッセージをディフォルトから修正
    renderAuthMessage _ _ = loginMessage

    -- authManagerはそのまま
    authHttpManager = getHttpManager

-- ログイン後に出力されるメッセージ
loginMessage :: AuthMessage -> Text
loginMessage _ = "乱入ではなくログインしました"

-- Userモデルを認証用としSha1のパスワードで認証するように HashDBUserのインスタンスに設定
instance HashDBUser User where
  userPasswordHash = Just . userPassword
  setPasswordHash h u = u { userPassword = h }

Home.hsを修正


ログイン、ログアウト後のリダイレクト先を作成、POSTは使わないので削除

module Handler.Home where

import Import

-- This is a handler function for the GET request method on the HomeR
-- resource pattern. All of your resource patterns are defined in
-- config/routes
--
-- The majority of the code you will write in Yesod lives in these handler
-- functions. You can spread them across multiple files if you are so
-- inclined, or create a single monolithic file.

getHomeR :: Handler Html
getHomeR = do
  maid <- maybeAuthId
  let handlerName = "getHomeR" :: Text
  defaultLayout $ do
    setTitle "HashDB認証サンプル"
    $(widgetFile "homepage")

テンプレート類の修正


色々削除してしまったので、不要な展開変数やタグを削除

homepage.hamlet

<h2>ログインサンプル
$maybe uid <- maid
    <p>#{show uid}
        <a href=@{AuthR LogoutR}>ログアウト
$nothing
    <p>
        <a href=@{AuthR LoginR}>ログイン画面へ
homepage.julius
全て削除
 
homepage.lucius
h1 {
    text-align: center
}
h2 {
    color: #990
}

cabalファイルに依存パッケージを追加


YesodAuthSample.cabalファイルを修正

~/Code/YesodAuthSample $ git diff YesodAuthSample.cabal
diff --git a/YesodAuthSample.cabal b/YesodAuthSample.cabal
index 0bc29ae..f196287 100644
--- a/YesodAuthSample.cabal
+++ b/YesodAuthSample.cabal
@@ -79,6 +79,7 @@ library
                  , containers
                  , vector
                  , time
+                 , yesod-auth-hashdb
 
 executable         YesodAuthSample
     if flag(library-only)
~/Code/YesodAuthSample $ 

スキーマを作成


yesod develを実行すればマイグレーションして自動でスキーマを作成してくれる

cuomo@karky7 ~/Code $ cd YesodAuthSample
cuomo@karky7 ~/Code/YesodAuthSample $ mysqladmin -u root create YesodAuthSample
cuomo@karky7 ~/Code/YesodAuthSample $ yesod devel
Yesod devel server. Press ENTER to quit
Warning: The package list for \'hackage.haskell.org\' does not exist. Run \'cabal
update\' to download it.
Resolving dependencies...
Configuring YesodAuthSample-0.0.0...
Rebuilding application... (using cabal)
Starting development server...
Starting devel application
Migrating: CREATe TABLE `user`(`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,`email` TEXT CHARACTER SET utf8 NOT NULL,`password` TEXT CHARACTER SET utf8 NOT NULL)
30/Dec/2014:12:00:06 +0900 [Debug#SQL] \"CREATe TABLE `user`(`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,`email` TEXT CHARACTER SET utf8 NOT NULL,`password` TEXT CHARACTER SET utf8 NOT NULL)\" [] @(persistent-2.1.1:Database.Persist.Sql.Raw ./Database/Persist/Sql/Raw.hs:55:18)
Migrating: ALTER TABLE `user` ADD CONSTRAINT `unique_user` UNIQUE(`email`(200))
Devel application launched: http://localhost:3000
30/Dec/2014:12:00:06 +0900 [Debug#SQL] \"ALTER TABLE `user` ADD CONSTRAINT `unique_user` UNIQUE(`email`(200))\" [] @(persistent-2.1.1:Database.Persist.Sql.Raw ./Database/Persist/Sql/Raw.hs:55:18)
cuomo@karky7 ~/Code/YesodAuthSample $

ログインアカウントデータを挿入

sha1でhash化する

cuomo@karky7 ~/Code/YesodAuthSample $ mysql -u root YesodAuthSample
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 14
Server version: 5.6.21-log Source distribution
...
...
...
mysql> INSERT INTO user(email, password) VALUES('karky7@sample.jp', sha1('karky7'));
Query OK, 1 row affected (0.37 sec)

mysql> quit
Bye

動かしてみる

cuomo@karky7 ~/Code/YesodAuthSample $ yesod devel

初期画面

ログイン前はログイン画面へのリンクを表示する


ログイン画面

「http://localhost:3000/auth/login」というURLでログイン画面が出力される、先ほどINSERTした認証でログインを実行する


ログイン完了

見た目は悪いのですがログイン完了の画面、URLもHomeRへ戻り、getAuthIdで取得できた認証情報を表示している。User情報のIDを表示しているだけですが。


こんな感じで認証ができる、ログインフォームまで作ってくれるので楽です、細かい動作や出力の調整はクラスの関数をオリジナルのものへ修正すれば可能なので、基本的にディフォルトの動作は実装されています。

コードを書いているとほとんどの悪い箇所をコンパイラが拾ってくれるので凄く楽。Haskell+Yesodが今のところ、一番のお気に入りフレームワークかな。



0 件のコメント:

コメントを投稿