
それは過小評価されているfeathersjsフレームワークについてです。
一言で言えば、それがどのように機能するかは、 ここで読むことができます 。 ある朝、メッセンジャーで新しいTKが届きました。次のように説明されました。2つのサービス(つまり、バックエンドが2つの異なるフロントからの認証要求を処理するため)にユーザーを分ける必要があります。
つまり、VueJsで書かれた2つの分割されたフロント、異なるドメイン名に配置され、共通のバックエンドが羽で書かれています(そして当然、ロールを持つデータベース内の1つのユーザーテーブル)。
つまり、テーブルには2つのそのようなフィールドが含まれる場合があります
email pass project +------------+--------+------- 1@gmail.com 123 front_one 1@gmail.com 123 front_two
当然、パスワードはハッシュされます。
サービスに入るためのモジュールとして、 feathers-authentication ( passportjsの羽に適合したバージョン)を使用します。
したがって、ローカルログインに関しては、すべてが簡単です。 ログイン/パスワードのペアを持つリクエストがどこに来たのかを判断するために、「project」などの別のパラメータをリクエスト本文の先頭に挿入し、このパラメータを使用してデータベースで必要なユーザーを検索できます。
ローカル認証ユーザーの作成方法についてもう少し詳しく説明します。 認証用の別のファイル(ファイルであり、サービスを生成しませんでした)auth.jsを作成し、app.jsに接続しました。 auth.jsはこのようになります
const authentication = require('feathers-authentication'); const jwt = require('feathers-authentication-jwt'); const local = require('feathers-authentication-local'); const oauth2 = require('feathers-authentication-oauth2'); const FacebookStrategy = require('passport-facebook'); const commonHooks = require('feathers-hooks-common'); module.exports = function () { const app = this; const config = app.get('authentication'); app.configure(authentication(config)); app.configure(jwt()); app.configure(local(config.local)); app.service('authentication').hooks({ before: { create: [ commonHooks.lowerCase('email'), authentication.hooks.authenticate(config.strategies) ], remove: [ authentication.hooks.authenticate('jwt') ] } }); };
一般的に、今は何も面白いものはありません。私が注意したいのは、フックを個別のファイルに入れなかったということです(標準の生成サービスを呼び出すときに起こります)。それは何のためですか。
次に、魔法を追加します。 ドキュメントを調べてみると、verifier classが見つかりました 。これは拡張して機能に追加できます。 ローカル認証用の新しい構成を匿名関数に追加しました
app.configure(local({ Verifier: CustomVerifier }));
そして、私の新しいクラスを呼び出しました
class CustomVerifier extends Verifier { verify(req, username, password, done) { return this.app.service('users').find({ query: { email: username, roles: req.query.project } }).then(res => { const user = res.data[0]; if (user) { const userId = res.data[0].id; this._comparePassword(user, password).then(() => { if (!user.isVerified) { done(null) } else { done(null, user, { userId: userId }); } }).catch(err => { done(null) }) } else { done(null) } }) } }
このクラスは何をしますか? まず、ユーザーサービスthis.app.service( 'users')に接続し、クエリパラメーターを指定してfindメソッドを呼び出します。 つまり、2つのフィールドで必要なユーザーのデータベースを検索し、それが見つかった場合、答え(res変数)にユーザーの配列があり、ユーザーが見つからなかった場合、配列は空を返します。 次に、f-tionを呼び出します
this._comparePassword()
ここでは、ユーザーが見つけたパラメーターと、前面から送られたパスワードを渡します。 _comparePassword関数はパスワードをハッシュし、それをデータベース内のパスワードと比較します。パスワードが一致する場合は、次に呼び出します()
done(null, user, { userId: userId });
ここで、最初の引数はエラーオブジェクト、2番目は現在のユーザー、3番目はデータベース内のユーザーID、done()は正しいトークンを返します。 null引数のみをdone()に渡すと、リクエストのステータスは401になり、レスポンスでは次のようになります
lassName:"not-authenticated" code:401 errors:{} message:"Error" name:"NotAuthenticated"
そして、それで終わりです。しかし、facebook経由で私たちのサービスにアクセスすることもできます。 これを可能にするには、匿名関数に次を追加する必要があります。
app.configure(oauth2(Object.assign({ name: 'facebook', Strategy: FacebookStrategy, Verifier: CustomVerifierFB }, config.facebook)));
このコードでも、「Verifier:CustomVerifierFB」というパラメーターのみに関心があります。 ローカル登録の場合と同様に、組み込みのVerifierクラスを拡張します。 fbを介してログインすると、フロントはバックの特定のURLにリクエストを送信しませんが、リンクをクリックします。つまり、フロントでは次のようになります。
<a href="/auth/facebook"> Fb</a>
簡単に言えば、リンクをクリックすると、バックエンドへのリダイレクトが発生し、バックエンドはFBにリダイレクトされ、FBはバックエンドにリダイレクトされ、バックエンドは生成されたトークンをCookieに書き込み、フロントページに送信します。 前面では、Cookieを解析し、次のリクエストを新しいトークンで背面に送信する必要があります。
そして、この記事はありませんが、私は質問に多くの時間を費やしました-しかし、ユーザーがどこから来たのかを実際に調べる方法は?
答えは非常に簡単でした。 FBを介して入力するコンポーネントを登録する前に、これを行う必要があります。
app.get('/auth/facebook', (req, res, next) => { referOrigin = req.headers.referer next(); }) app.configure(oauth2(Object.assign({ name: 'facebook', Strategy: FacebookStrategy, Verifier: CustomVerifierFB }, config.facebook)));
つまり、「/ auth / facebook」への移行をキャッチし、値req.headers.refererをグローバル変数(referOrigin)に書き込み、oauth2()の登録を開始します。 したがって、グローバル変数でホスト値を取得し、CustomVerifierFBクラスでこの値を使用できます。これは次のようになります。
class CustomVerifierFB extends Verifier { verify(req, accessToken, refreshToken, profile, done) { const refer = referOrigin let roles = '' if (refer === 'front_one') { roles = 'front_one' } else { roles = 'front_two' } return this.app.service('users').find({ query: { facebookId: profile.id, roles: roles } }).then(res => { if (res.data[0]) { done(null, res.data[0], { userId: res.data[0].id }); } else { return this.app.service('users').create({ facebookId: profile.id, email: profile._json.email, first_name: profile._json.first_name, last_name: profile._json.last_name, gender: profile._json.gender, avatar: profile._json.picture.data.url, roles: roles, isVerified: true, username: profile._json.email + 'whereFromUser' }).then(createRes => { done(null, createRes, { userId: createRes.id }); }) } }) } }
検証では、次のことを行いました。
- this.app.service( 'users')。find()-データベースにfacebookIdを持つユーザーが存在するかどうかを検索します。
- done(null、res.data [0]、{userId:res.data [0] .id})-存在する場合、新しいトークンを作成し、それを最前面に返します
- this.app.service( 'users')。create()見つからない場合は、そのようなユーザーを作成してからdone()を呼び出します
これにより、2つの異なる分野でユーザーを分離する問題を解決しました。
PS-次に、2つのユーザーグループのパスワード回復方法を書きます。