このレッスンは、 derbyjsの一連のレッスンの継続-リアクティブフルスタックフレームワークです。 必要な以前のレッスンを読んでください( first 、 second )。
このレッスンは2つの部分で構成されます。1つ目は、ダービーアプリケーションのサーバー側の概要であり、2つ目は、ソーシャルネットワークを使用したダービーアプリケーションでの登録/承認のための
derby-auth
モジュール(
passportjs
ラッパー)の使用です。
パート1-ダービーアプリケーションのサーバーコード
準備する
前の例では、
derby-starter
を使用して、サーバー部分としての情報の認識を促進しました。 このモジュールは、サーバー設定の詳細を隠しました。 サーバーでは、ダービーアプリケーションが標準のexpressjsアプリケーションの上に構築されていることを思い出させてください。特に、データベースがそこに構成され、必要な高速ミッドウェアが接続されています。 現在の例では、サーバー側をより深く処理する必要があります
derby-starter
は使用しません。
誰かが
expressjs
精通していないか、
expressjs
よく知らない場合は、Ilya Kantorのすばらしいコース
nodejs
お勧めします。
基本アプリケーション
今日の例のベースとして、
derby-boilerplate
をコピーする必要があり
derby-boilerplate
。 実際、これは最初の例と同様の最小限のアプリケーションですが、サーバー部分は
derby-starter
ではなくプロジェクト自体にあります(最後の4番目のエクスプレスもここで使用され、
redis
はまったく使用されません) -今ではそれなしでも可能です。 コマンドをコピーします。
git clone https://github.com/derbyparty/derby-boilerplate.git
プロジェクトの構造を調べてみましょう-前の例とは異なり、それはすでにかなり戦闘です:
src/ app/ server/ styles/ views/ index.js
app
フォルダーには、ダービーアプリケーションがあります。 概略的には、サーバーフォルダーに
derby-starter
「アナログ」があり(
derby-starter
を実際にコピーし、そこにコピーして少しコーミングしました)、その内容をより詳細に分析します。
styles
と
views
フォルダー、スタイルとテンプレートにそれぞれ。 index.jsファイルは、前の例の内容と似ています-全体を開始する数行です。
ダービーアプリケーションのサーバー部分
まず第一に、derbyが
expressjs
アプリケーション上で動作するという事実に再び注目します。そのため、それが何であるか、
express-middleware
何か、どのように動作するか、エクスプレスセッションの動作、ルーティングがわからない場合
express-middleware
、ここで停止する必要があります知識のギャップを解消します。
サーバーフォルダーにあるものを見てみましょう。
error/ index.js server.js
エラーハンドラーをエラーフォルダーに配置しましたが、ここにはいません。 これは
expressjs-middlware
であるとしか言えません。これは、型指定された
url
ハンドラーがない場合、またはアプリケーションの操作中にエラーが発生した場合にトリガーされます。 あなた自身のためにそれを把握したい場合。
index.js
ファイルの主な目的は、(
server.js
ファイルで構成された)
express
を選択し、設定で指定されたポートでサーバーを上げることです。
ソースコード(derby-starterから取られた実質的に変更なし)
var derby = require('derby'); exports.run = function (app, options, cb) { options = options || {}; var port = options.port || process.env.PORT || 3000; derby.run(createServer); function createServer() { if (typeof app === 'string') app = require(app); var expressApp = require('./server.js').setup(app, options); var server = require('http').createServer(expressApp); server.listen(port, function (err) { console.log('%d listening. Go to: http://localhost:%d/', process.pid, port); cb && cb(err); }); } }
次に、
derby
追加してexpressjsを設定する楽しい部分があります。 この点を理解することは、ダービーの仕組みを理解するための鍵です。
私は4番目のエクスプレスを使用しますが、3番目とはいくつかの違いがあります。 主なことは、標準のmiddlwareが
express
ディストリビューションに含まれていないため、個別にインストールする必要があることです。
したがって、このファイルはダービーなしのようになります。
Server.jsコード、通常のexpressjsアプリケーションを作成する場合:
var express = require('express'); // 4- middleware // var session = require('express-session'); // var MongoStore = require('connect-mongo')(session); // - , // var midError = require('./error'); var MongoClient = require('mongodb').MongoClient; exports.setup = function setup(app, options) { var mongoUrl = process.env.MONGO_URL || process.env.MONGOHQ_URL || 'mongodb://localhost:27017/derby-app'; // MongoClient.connect(mongoUrl); var expressApp = express() if (options && options.static) { expressApp.use(require('serve-static')(options.static)); } expressApp.use(require('cookie-parser')()); expressApp.use(session({ secret: process.env.SESSION_SECRET || 'YOUR SECRET HERE', store: new MongoStore({url: mongoUrl}) })); // - // - 404 expressApp.all('*', function(req, res, next) { next('404: ' + req.url); }); // expressApp.use(midError()); return expressApp; }
一言で言えば、これがどのように機能するかを思い出させてください。 最初に、モジュールが接続され、次に最も重要なこと-エクスプレス
middleware
接続(
expressApp.use
経由)。 実際、これらの
middlware
は、サーバーに到着する各要求に対して、登録された順序と同じ順序で呼び出される単純な関数です。 これらの各
middleware
は、処理チェーンを完了することで要求に応答するか(残りのmiddlwareに制御が転送されない)、要求を使用して中間アクションを実行し(
cookies
解析、
cookies
によるセッションの決定など)、制御を転送できますさらにチェーンを下る。
middlware
の接続順序
middlware
非常に重要です。このシーケンスでは、各リクエストに対して関数が呼び出されます。
そして、ここにダービーオプションがあります-ダービーでコメントされたserver.jsコード
// 4- var express = require('express'); // 4- middleware // var session = require('express-session'); // var MongoStore = require('connect-mongo')(session); // - , // var midError = require('./error'); var derby = require('derby'); // BrowserChannel - socket.io - , // , // liveDbMongo - - var racerBrowserChannel = require('racer-browserchannel'); var liveDbMongo = require('livedb-mongo'); // browserify derby.use(require('racer-bundle')); exports.setup = function setup(app, options) { var mongoUrl = process.env.MONGO_URL || process.env.MONGOHQ_URL || 'mongodb://localhost:27017/derby-app'; // ( redis) var store = derby.createStore({ db: liveDbMongo(mongoUrl + '?auto_reconnect', {safe: true}) }); var expressApp = express() // "" // (.. /derby/...) expressApp.use(app.scripts(store)); if (options && options.static) { expressApp.use(require('serve-static')(options.static)); } // browserchannel, // middleware, // (browserchannel longpooling - .. // /channel) expressApp.use(racerBrowserChannel(store)); // req getModel, // express- // . createUserId expressApp.use(store.modelMiddleware()); expressApp.use(require('cookie-parser')()); expressApp.use(session({ secret: process.env.SESSION_SECRET || 'YOUR SECRET HERE', store: new MongoStore({url: mongoUrl}) })); expressApp.use(createUserId); // -, // , // expressApp.use(app.router()); // - // // - 404 expressApp.all('*', function(req, res, next) { next('404: ' + req.url); }); // expressApp.use(midError()); return expressApp; } // id- , // id - function createUserId(req, res, next) { var model = req.getModel(); var userId = req.session.userId; if (!userId) userId = req.session.userId = model.id(); model.set('_session.userId', userId); next(); }
少し時間をかけてそれを理解してください。 実際、ここでの主なことは
browserify
使用目的です-クライアントにいわゆる「バンドル」をクライアントに提供する必要があります-ダービーアプリケーションのすべての
javascript
ファイルとテンプレートを含むデータブロック(cssスクリプトは含まれません) 。 クライアントによるサイトのページの定期的なリクエストでは、バンドルがブラウザにすぐに送信されないため、コストが高すぎることを理解する必要があります。 スタイルと初期データを含むレンダリング済みのページが推奨されます。 ここでは速度が非常に重要なので、これは論理的です。 さらに、このページ自体が「バンドル」をロードします-アドレス「/ derby /:bandle-name」で要求が行われます。
複数の「ダービーアプリケーション」がダービーの1つのサーバー部分に対応できることは既に述べました。実際、それぞれに独自のバンドルがあります。 これは論理的です。 したがって、たとえば、サイトのクライアント部分を管理パネルから分離して、不要なデータを全員に転送しないようにすることができます。 その場合は、登録する必要があります。
expressApp.use(clientApp.scripts(store)); expressApp.use(adminApp.scripts(store));
同じことがルートの処理にも当てはまります。 uに2つの「ダービーアプリケーション」がない場合、代わりに次のようになります。
expressApp.use(app.router());
2つのアプリケーションルーターの接続:
expressApp.use(clientApp.router()); expressApp.use(adminApp.router());
また、「ダービーアプリケーション」ルートのハンドラーに加えて、ここに
expressjs
ハンドラーを追加して、ブラックジャックや若い女性で必要なRESTFull APIを整理できることも理解しておく必要があります。 これはすべて、ダービーアプリケーションのクライアントルーターが、アプリケーションで目的のハンドラーを見つけられず、サーバーに処理の要求を送信するだけで機能するためです。
次の重要なポイントは、
browserchannel
の接続です。これは、
socket.io
socket.io
です。 実際、これはトランスポートです。これにより、アプリケーションがリアルタイムでサーバーとデータを同期します。 内部では、特定の瞬間にクライアントスクリプトbrowserchannelがバンドルに追加され、このモジュールからのリクエストを処理します(ロングプーリングに基づいているため、この場合は
/channel
での処理が必要です)
はい、ダービーの基本的なセットアップ例では、
redis
は使用されないことに注意してください。 ダービーのアップデートが最近リリースされましたが、今では
redis
必要
redis
ません(水平スケーリングを行う場合、つまりパフォーマンスを向上させるために複数のダービーサーバーを一度に実行する場合に必要です)。
したがって、現時点では、この構造を使用すると、非常に柔軟にアプリケーションに何かを追加できることを理解する必要があります。 何らかの種類の
expressjs-middlware
、ダービープラグイン、個々のダービーアプリケーション、エクスプレスハンドラーなどです。 これらはすべて一緒に静かに暮らすでしょう。
それで、特に認可の問題を考慮して、セッションに注意を払う必要がある最後のポイントです。 ご覧のとおり、ダービーセッションは非常に
express
なセッションを使用します。 つまり、すべては古典的なスキームに従って行われます
mongodb
は識別に使用され(クライアントのブラウザに保存されます)、セッション自体は
sessions
コレクションの
mongodb
に保存され
sessions
。
createUserId
関数を見ると:
function createUserId(req, res, next) { var model = req.getModel(); var userId = req.session.userId; if (!userId) userId = req.session.userId = model.id(); model.set('_session.userId', userId); next(); }
userIdはそのセッションから取得され、存在しない場合はidがランダムに生成され(
model.id()
)、パス
_session.userId
(このデータはシリアル化されてクライアントに送信されます)とセッション自体に沿ってモデルに書き込まれます。
いくつかのクエリオプションを想像してください。
- ユーザーが最初にサイトを訪問し、メインページを要求しました-新しいCookieがサーバー上に形成され、新しいセッション(新しいランダム
userId
)-メインページがブラウザーに描画され、クライアントでモデルを見ると、パス '_session.userId'に沿って-新しく形成されたIDが表示されます。 アプリケーションのページをさまよいます(サーバーへのリクエストはありません)-クライアントは常にそのIDを持ち、サーバーへのリクエストを行う場合、クッキーとセッションで常にそれを認識できます。 - ユーザーは1週間後に登録およびログインしませんでした。 Cookieは彼から保存されます-彼には同じ
userId
が与えられます-すべては問題ありません。 - ユーザーが別のブラウザーからログインした-ステップ1と同じ
(サーバーの観点から)登録/承認をどのように開発するとします。 クライアントがブラウザに登録データを入力したとします-その後、ユーザーコレクションにエントリを作成するサーバーに移動する必要があります。IDを作成する必要はなく、既に存在します。
承認がさらに困難になると、サイトにアクセスするクライアントはランダムIDを受け取り、承認ページに移動します-ユーザー名/パスワードを入力し、サーバーにすべて転送します。サーバーは、セッションと
_session.userId
userIdを変更する必要があることを理解します
このコレクションに何を保存しますか。 最も可能性が高いのは、ユーザー名、メール、passwordHash、独自の統計情報などです。 私たちのサイトにソーシャル経由の承認(登録)がある場合。 ネットワーク、ソーシャルネットワークのキーはここに保存されます。 また、もちろん、ユーザー設定もここに保存しておくといいでしょう。
当然、このコレクションの一部をクライアントで表示できないようにしてください。 これで問題はありません。 最近、いわゆるプロジェクションがダービー(コレクションにサブスクライブする機能、ドキュメント全体ではなく、その特定のフィールドのみにサブスクライブする機能)に登場しましたが、数週間前-それは不可能でした、そしてこれについて検討する
derby-auth
モジュールまだできません。 ユーザーデータを2つの部分に分けて使用します
auths
目は
auths
コレクション(ここではプライベートデータのみ)、2つ目の
users
はここで公開データです。 だから
derby-auth
パート2-ダービーアプリケーションでの承認(derby-authモジュール)
ダービー認証について
現時点では、2つのパッケージがあります-ダービーの承認。 これらは
derby-auth
と
derby-passport
です。 どちらも
passportjs
ラッパーです(私の意見では、そのうちの1つは別のフォークです)。 両方の現在のバージョンはダービーの5番目のバージョン(アルファで6番目のバージョンを思い出す)で使用することを目的としており、6日でまだ更新されていませんが、6-keのすべてのプロジェクトで
derby-auth
を使用することを妨げませんhtmlフォームテンプレートのみですが、いずれにしてもカスタムです)。
私が
derby-auth
を選択したのは、それを使ったからです。
既に言及した別の制限は、これらのプロジェクトの両方のユーザーデータが2つの部分に分割されることです。 パブリックデータは
users
コレクションに保存し、
auths
プライベートデータを保存する必要があり
users
。 最近、ダービーには「投影」、つまりコレクションの特定のフィールドのみをサブスクライブする機会があります。これらのモジュールがダービーの第6バージョンに更新されると、制限はなくなると思います。
更新する
この記事を書いてから数日後、Vladimir Makhaev(@vmakhaev)はderby-authに基づいた素晴らしいderby-login認証モジュールを開発しました。 0.6未満で、プロジェクションをサポートし、登録、入力、パスワードの変更のための既製のコンポーネントを備えています-要するに、それだけを使用します。
ちなみに、少なくともgithubにアスタリスクを付けて、ウラジミールに感謝することを忘れないでください。 彼はとても便利なことをしました。
passportjsについて
PassportJS
は、
node.js
での承認用
middleware
です。 これは、アプリケーションがログインとパスワードを使用した通常の許可と、Oauth、Oauth 2などをサポートするほぼすべてのサービスによる許可の両方を使用できるようにする抽象モジュールです。 認可。 ここでは、承認方法を戦略と呼びます。 たとえば、ログイン/パスワード認証は
LocalStrategy
(「ローカル認証戦略」)と
GithubStrategy
、GitHub認証は
GithubStrategy
と
GithubStrategy
。 各戦略は個別のモジュールに配置されるため、この全体をnode.jsアプリケーションに接続すると次のようになります。
var passport = require('passport'); var LocalStrategy = require('passport-local').Strategy; var GithubStrategy = require('passport-github').Strategy;
また、ソーシャルネットワークを介して承認戦略を使用する機能にも注意する必要があります。 他の誰かが知らない場合、アプリケーションがそのような承認を使用する前に、ソーシャル(アプリケーション)に登録する必要があります。 ネットワークと2つのパラメーターを取得します。
appId
(または
clientId
はこのソーシャルネットワーク内のアプリケーションの一意のid-shnikです)とSecret(誰にも言えない秘密鍵、アプリケーションが確認できるように使用されます)私たちは私たちです)。 登録時には、いわゆる
redirectURL
、つまりソーシャルのURLアドレスを入力する必要があります。 ネットワークは認証後にクライアントのブラウザをリダイレクトします。 私の例では、これは次のようになります: localhost :3000 / auth / github / callback-しかし、これについては後で詳しく説明します。
サンプルアプリケーションでは、
github
を介して承認を使用し、アプリケーションを登録するのは非常に簡単です(他のプロバイダーにとってはそれほど難しくありません)。 まず、通常のユーザーアカウントが必要です。設定でアプリケーションを簡単に作成できます。
誰にも私の秘密を教えないでください。
derby-authをアプリケーションに接続します
そのため、まず、gitリポジトリから
derby-auth
直接配置します。何らかの理由で、以前の
derby
マスターバージョンがあります。
npm install git://github.com/cray0000/derby-auth#0.5 -S
Derby-auth
自動的に
passport
と
passport-local
、
passport-github
をインストールします:
npm install passport-github -S
次に設定に進みます。 https://github.com/lefnire/derby-auth/tree/0.5の指示に従ってすべてを行います 。 接続アクションのほとんどはサーバーで実行されます。つまり、src / server / server.jsが必要です。
ステップ1
derby-auth自体を接続し、戦略を設定します(通常のアプリケーションでは、設定は設定ファイルまたは環境変数から取得する必要があります)
// derby-auth var auth = require('derby-auth'); var strategies = { github: { strategy: require("passport-github").Strategy, conf: { clientID: 'eeb00e8fa12f5119e5e9', clientSecret: '61631bdef37fce808334c83f1336320846647115' } } }
より多くの戦略がある場合、それらのすべてがここにリストされることは明らかです:
ステップ2
オプションを設定します。
var options = { passport: { failureRedirect: '/login', successRedirect: '/' }, site: { domain: 'http://localhost:3000', name: 'Derby-auth example', email: 'admin@mysite.com' }, smtp: { service: 'Gmail', user: 'zag2art@gmail.com', pass: 'blahblahblah' } }
原則として、すべては名前から明らかです:
failureRedirect
、
successRedirect
承認に
successRedirect
した場合/成功した場合にクライアントがリダイレクトされるURL。 サイトに関する情報は、戦略を送信するために必要です(たとえば、アプリケーションの登録時に対応するフィールドが入力されていない場合、ソーシャルネットワークで使用できます)。 「パスワードを忘れた場合-パスワードをリセットして、石鹸用の新しいパスワードを送信する」ためには、メールパラメータが必要です。
ステップ3
ストレージを初期化する
auth.store(store, false, strategies);
このステップで、
derby-auth
はデータにアクセス制御を追加します。ユーザーはauthsコレクションから他の人のレコードを取得できません-自分のアカウントのみが表示されます。 2番目のパラメーターは現在使用されていません。
ステップ4
, middleware
body-parser
( —
npm i body-parser -S
):
expressApp.use(require('cookie-parser')()); expressApp.use(session({ secret: process.env.SESSION_SECRET || 'YOUR SECRET HERE', store: new MongoStore({url: mongoUrl}) })); expressApp.use(require('body-parser')()) // method-override , // , PUT // expressApp.use(require('method-override')())
—
derby-auth middeware
— :
expressApp.use(auth.middleware(strategies, options));
, . — middleware, , -
expressjs
, url. — :
パラメータ | 行き先 | ||
---|---|---|---|
/login | post
| username, password | |
/register | post
| username, email, password | |
/auth/:provider | get
| . ネットワーク。
: /auth/github | |
/auth/:provider/callback | get
| . — derby-auth failureRedirect successRedirect url.
: /auth/github/callback — url , . ネットワーク。 | |
/logout | get
| «» '/' | |
/password-reset | post
| email ( AJAX) | |
/password-change | post
| uid, oldPassword, newPassword | ( AJAX) |
() — (html / ..), 0.5 . , , . .
Derby-auth
, , id, , :
させて | 説明 |
---|---|
_session.loggedIn | — / |
_session.userId | id |
_session.flash.error | — ( /) |
auth.{userId}.local | |
auth。{userId}。{provider} | 関連するソーシャルを通じた登録データ。 ネット |
これらはすべてテンプレートで利用でき、承認ステータスを判断するために使用されます。
応用例
一般に、もちろん、ビジュアルコンポーネント
derby-auth
がダービーの6番目のバージョンに適合している場合、アプリケーションは2つのアカウントで記述されますが、それなしではより困難です。開発を簡素化するために、接続
bootstrap
します。このために、src / app / index.jsに入力します
app.use(require('d-bootstrap'));
出来上がり
npm i d-bootstrap -S
.
2 : , "/" , , , , '/login' , . ,
auths
.
-
src/app/index.js
, , , "*", :
var derby = require('derby'); var app = module.exports = derby.createApp('auth', __filename); global.app = app; app.use(require('d-bootstrap')); app.loadViews (__dirname+'/../../views'); app.loadStyles(__dirname+'/../../styles'); app.get('*', function(page, model, params, next){ var user = 'auths.' + model.get('_session.userId'); model.subscribe(user, function(){ model.ref('_page.user', user); next(); }); }); app.get('/', function (page, model){ page.render('home'); }); app.get('/login', function (page, model){ page.render('login'); });
—
layout
c
index.html
,
home.html
login.html
, , (/ , github-), /.
, index.html
<import: src="./home"> <import: src="./login"> <Title:> Derby-auth example <Body:> <div class="navbar navbar-inverse" role="navigation"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="/">Derby-auth Example</a> </div> <div class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <view name="nav-link" href="/"></view> <view name="nav-link" href="/login"></view> </ul> </div> </div> </div> {{if _session.flash.error}} <div class="container"> {{each _session.flash.error as #error}} <div class="alert alert-warning">{{#error}}</div> {{/}} </div> {{/}} <view name="{{$render.ns}}"></view> <nav-link: element="nav-link"> <li class="{{if $render.url === @href}}active{{/}}"> <a href="{{@href}}">{{@content}}</a> </li>
— ( alert-). , , :
<view name="{{$render.ns}}"></view>
home.html
,
login.html
, , :
page.render('home')
page.render('login')
,
home.html
:
<index:> <div class="container"> <h1>:</h1> {{if _session.loggedIn}} <p> </p> <h2> :</h2> {{if _page.user.local}} <p> : <b>{{_page.user.local.username}}</b></p> {{else}} <p> </p> {{/}} <h2>GitHub:</h2> {{if _page.user.github}} <p> : <b>{{_page.user.github.username}}</b></p> {{else}} <p> </p> {{/}} <a class="btn btn-danger" href="/logout"></a> {{else}} <p> </p> <a href="/login">/</a> {{/}} </div>
«» —
GET /logout derby-auth
, «sign in» http://getbootstrap.com/examples/signin/
- slyles/index.styl
.form-signin { max-width: 330px; padding: 15px; margin: 0 auto; } .form-signin .form-signin-heading, .form-signin .checkbox { margin-bottom: 10px; } .form-signin .checkbox { font-weight: normal; } .form-signin .form-control { position: relative; height: auto; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; padding: 10px; font-size: 16px; } .form-signin .form-control:focus { z-index: 2; } .form-signin input[type="email"] { margin-bottom: -1px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .form-signin input[type="password"] { margin-bottom: 10px; border-top-left-radius: 0; border-top-right-radius: 0; }
login.html :
<index:> <view name="signin"></view> <hr/> <view name="signup"></view> <signin:> <div class="container"> <form class="form-signin" role="form" action='/login' method='post'> <h3 class="form-signin-heading"></h3> <input name="username" type="text" class="form-control" placeholder="" required="" autofocus=""> <input name="password" type="password" class="form-control" placeholder="" required=""> <button class="btn btn-lg btn-primary btn-block" type="submit"></button> <br/> <a class="btn btn-lg btn-danger btn-block" href="/auth/github"> GitHub</a> </form> </div> <signup:> <div class="container"> <form class="form-signin" role="form" action='/register' method='post'> <h3 class="form-signin-heading"></h3> <input name="username" type="text" class="form-control" placeholder="" required="" autofocus=""> <input name="email" type="email" class="form-control" placeholder="" required=""> <input name="password" type="password" class="form-control" placeholder="" required=""> <button class="btn btn-lg btn-primary btn-block" type="submit"></button> </form> </div>
-:
, derby-auth . : post /login — derby-auth, username password — . GitHub — get /auth/github — .
. — post /register, : username, email, password.
: npm start — — « », , — « ». — /logout. github-, « github», , , .
Derby-auth , , . ネットワーク。 , , . , derby-auth .
, . , ( app.model.get() ).
,
derby-auth
( ) — - .
, , , .
github
, - , , . — . , .
PS
derbyjs, : zag2art . — ( ) , .
PPS
, 0.6 alpha 7 — .
derbyjs — github