buddyのユーザ認証周りのデータ構造 -...
TRANSCRIPT
buddyのユーザ認証周りのデータ構造使われているデータの取り回しについてのお話
2017/11/17clj-ebisu #1
出演
{:company “Greative.GK” :name “Kazuhiro Hara” :twitter “@kara_d” :interest “SPA, WebVR, Clojure, Design research”}
Clojure / ClojureScript で
Electronアプリケーションを
作るためのスタートキット / プラットホーム
● オープンソースにてGitHubにて公開
● MITライセンス
● 現在のスター数 : 329http://descjop.org/
今日お話しすること
今日お話しすること
buddy-authのidentity実装をみてたときに、
リクエストマップに対するmiddlewareの使い方で
アハ体験した話
buddyってなにさ、というあたりから
buddyについて
buddy is a complete security library for clojure. With support for:
● authentication, authorization & access rules (ring/compojure extensions)
● secure hash functions (digest)
● password hashing algorithms (bcrypt, pbkdf2, scrypt)
● message/text signing (high level interface)
● signature & authentication (mac & digital signature)
● encryption (block, stream ciphers, nonces, salts)
● key derivation functions (kdf)
buddyのパッケージ
● buddy-core
● buddy-auth
● buddy-hashers
● buddy-sign
buddyのパッケージ
● buddy-core
● buddy-auth ⇦ 今日はこのあたりの話
● buddy-hashers
● buddy-sign
ユーザの認証・認可を実装するとき、お世話に
● 認証スタイルも、Basic、セッション、トークン、JWS、JWEといろいろ
● 特定ルート以下に適用するとかも楽
(def access-rules {:pattern #"^/admin.*" :handler authenticated-user?})
(-> app-routes (wrap-access-rules {:rules [ access-rules]}) (wrap-authentication backend) (wrap-authorization backend) (wrap-session {:store (session-cookie/cookie-store {:key key}) :cookie-attrs attr :cookie-name name})
buddy-authを使った認証の一例セッションベース
buddy-authを使った認証の一例(リクエスト編)
1. 認証対象のパスへアクセス( /admin とか)
2. セッションから認証済みかどうか確認
3. 認証済みであれば、リクエストマップへ :identity キーを追加3.1. 必要な処理をして、レスポンスを返す
4. 認証済みでなければ、:unauthorized-handler を実行4.1. エラー表示など
(defn wrap-authentication [handler & backends] (fn [request] (if-let [authdata (authenticate-request request backends)] (handler (assoc request :identity authdata)) (handler request))))
buddy-authを使った認証の一例(ログイン編)
1. ログインフォームから、認証に必要な情報を送信
2. 認証に必要な情報を照会
3. 認証OKであれば :identity キーをセッションに書き込む3.1. 適切な処理を行う、もしくはリダイレクト
3.2. identity のセッションへの書き込みは、自前で行う
4. 認証NGであれば、ログインフォームに戻るなどの処理をする
(-> (redirect next) (assoc :session updated-session))
認証されたリクエストかの確認
authenticated?関数でチェックができる
やっていることはシンプル
(defn authenticated-user? [request] (if (auth/authenticated? request) (accessrules/success) (accessrules/error)))
(defn authenticated? [request] (boolean (:identity request)))
更に、identityについて
identityには何が格納できる?
よくあるサンプルでは「ユーザー名」
中身は、edn形式の {:identity :admin} になる
そこで生まれる疑問、ユーザー名じゃないとだめなのか
結局、edn形式になるので、ednとして表現できるものならなんでもつっこめる
identityにマップを入れたらどうなる?
:identityに {:username :user, :role :admin} を入れてみる
cookieのセッション領域には、H/XbI0K3JU9TGMoyoGN16Ud2q4n8paIavLMFO+PQcqtXd3PtBIh9jmkg04FvqFuDXSYBlCbYAfMu8GhzE9ZFYQ==--BxvDyxfR0dtzjBiZqVPihT
ld/sLIYvI9hRycPtTQ1nM=などが入り、
中身は、{:identity {:username :user, :role :admin}} となる
identityに関数をつっこむと?
:identity に、例えば (fn [a] (println a)) を入れてみる
シリアライズに失敗してランタイムエラー
java.lang.RuntimeExceptionNo reader function for tag object
identityの不思議さ
今まで使ってきたWebアプリケーションフレームワークのリクエスト情報の利用スタイルで
は、リクエストオブジェクト的なものは、読みこんで利用するだけ
public Result index() { String username = session("identity"); if(username != null) { return ok("Hello " + username); } else { return unauthorized("Oops, you are not connected"); }}
よくあるリクエスト情報の処理
リクエストは、専用のコンテキストオブジェクトなどに格納され、それがコントローラを通じ
て、モデルなどで利用される
request controller
service
model
リクエストデータとミドルウェアパターン
最初、Clojureを触ったときは、requestには手を触れなかったが(ミドルウェアは利用して
いたものの)、変えるだけでなく、キーの追加もいいという驚き。
Middleware Patterns · ring-clojure/ring Wiki
https://github.com/ring-clojure/ring/wiki/Middleware-Patterns
request wrap-foo request wrap-bar request wrap-baz request
まとめ
ベルトコンベアのような認証プロセス
requestをもらって、コントローラ受け取って処理をして、responseを返すというスタイルだ
けでないやりかたに感銘を受けた。
個人的には、buddy-authのidentity実装には、Clojureで開発をしていく上でヒントになる
と感じたものが見れた。
フラグを立てるところと、フラグに基づいた処理をするところは別でもいい
受け取ってたらい回しにして処理するしかないようなものも、ミドルウェアとして組み合わ
せることで、独立性が高い実装が行える
こぼれ話
セッションの中身はどうなるのか?
セッションに書き込まれたidentityは信用できるか
ring.middleware.session.cookie に実装がある
● HmacSHA256○ SHA-256で構築された、ハッシュメッセージ認証コードとして使用可能なキー付きハッシュアルゴリ
ズムの一種
● AES/CBC/PKCS5Padding○ データ本体はAES暗号化
セッションの中を見る
開発側からは見れます
中身は、edn形式の {:identity :admin} になる
(let [cookieStore (session-cookie/cookie-store {:key "d41d8cd98f00b204"}) cookie-read (session-store/read-session cookieStore "LcgrPJu3zE0/Pvd0cSFo29jE/IlLlwRAa6Jo99FCZF9WKoJj/5bOLbp8g2mAB585--QB0LJInHfOCBH8kUwSIcOnrWqq7Ldk6wcHwinX/Kqug=")] (println (str cookie-read)))
CookieStore本体は見れない
ns-unmapされている
中身はこう
(deftype CookieStore [secret-key] SessionStore (read-session [_ data] (if data (unseal secret-key data))) (write-session [_ _ data] (seal secret-key data)) (delete-session [_ _] (seal secret-key {})))
(ns-unmap *ns* '->CookieStore)
- END -ありがとうございました
2017/11/17clj-ebisu #1