<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                passport用的比較多的有local本地驗證和OAuth驗證,這里講一下兩者的使用。 你也可以看看張丹寫的這兩篇,[Express結合Passport實現登陸認證](http://blog.fens.me/nodejs-express-passport/)和[Passport實現社交網絡OAuth登陸](http://blog.fens.me/nodejs-oauth-passport/),里面的示例覆蓋了基本的用法,本文也參考了其中的一些例子。 [TOC] ## local本地驗證 本地驗證默認使用用戶名和密碼來進行驗證。 ### 配置策略 在做驗證之前,首先需要對策略進行配置,官方的示例如下: ~~~ var passport = require('passport') ? , LocalStrategy = require('passport-local').Strategy; passport.use(new LocalStrategy( ? function(username, password, done) { ??? User.findOne({ username: username }, function(err, user) { ????? if (err) { return done(err); } ????? if (!user) { ??????? return done(null, false, { message: '用戶名不存在.' }); ????? } ????? if (!user.validPassword(password)) { ??????? return done(null, false, { message: '密碼不匹配.' }); ????? } ????? return done(null, user); ??? }); ? } )); ~~~ 其中的`User.findOne()`是MongoDB風格的語法,意思是從數據庫的User集合中查詢一條數據,第一個參數是查詢條件,后面是callback,一般在callback中進行后續操作。 這里的邏輯很簡單,依次檢查`username`、`password`,如果出錯則返回錯誤信息,如果通過則返回`done(null,user)`。 ### usernameField 前面說過passport默認使用用戶名和密碼來驗證,但實際上也有很多需要用郵箱來驗證的,那么如何實現呢? passport在策略配置里提供了options參數,用來設置你要驗證的字段名稱,即usernameField,使用方法如下: ~~~ passport.use(new LocalStrategy({ ??? usernameField: 'email', ??? passwordField: 'passwd' ? }, ? function(username, password, done) { ??? // ... ? } )); ~~~ 注意,這里的字段名稱應該是頁面表單提交的名稱,即req.body.xxx,而不是user數據庫中的字段名稱。 將options作為LocalStrategy第一個參數傳入即可。 ### 驗證回調 passport本身不處理驗證,驗證方法在策略配置的回調函數里由用戶自行設置,它又稱為驗證回調。驗證回調需要返回驗證結果,這是由done()來完成的。 在passport.use()里面,done()有三種用法: * 當發生系統級異常時,返回done(err),這里是數據庫查詢出錯,一般用next(err),但這里用done(err),兩者的效果相同,都是返回error信息; * 當驗證不通過時,返回done(null, false, message),這里的message是可選的,可通過express-flash調用; * 當驗證通過時,返回done(null, user)。 ### 密碼驗證 在張丹的教程里密碼是明文存儲的,在實際中這當然不行,上面的代碼里是user.validPassword(password)方法,這并不是passport添加的,而是需要用戶自定義。 一般對密碼進行哈希和鹽化的Nodejs模塊是bcrypt,它提供一個compare方法來驗證密碼,如何使用它則超出本文的范圍,這里就不講了。 ### ?session序列化與反序列化 驗證用戶提交的憑證是否正確,是與session中儲存的對象進行對比,所以涉及到從session中存取數據,需要做session對象序列化與反序列化。調用代碼如下: ~~~ passport.serializeUser(function(user, done) { ? done(null, user.id); }); passport.deserializeUser(function(id, done) { ? User.findById(id, function(err, user) { ??? done(err, user); ? }); }); ~~~ 這里第一段代碼是將環境中的user.id序列化到session中,即sessionID,同時它將作為憑證存儲在用戶cookie中。 第二段代碼是從session反序列化,參數為用戶提交的sessionID,若存在則從數據庫中查詢user并存儲與req.user中。 這段代碼的順序可以放在passport.use()的前面或后面,但需要在app.configure()之前。 ### Authenticate驗證 做完了上面這些設置,我們終于可以開始做驗證了。 ~~~ app.post('/login', ? passport.authenticate('local', { successRedirect: '/', ???? failureRedirect: '/login', ???? failureFlash: true }), ? function(req, res) { ??? // 驗證成功則調用此回調函數 ??? res.redirect('/users/' + req.user.username); ? }); ~~~ 這里的passport.authenticate(‘local’)就是中間件,若通過就進入后面的回調函數,并且給res加上res.user,若不通過則默認返回401錯誤。 authenticate()方法有3個參數,第一是name,即驗證策略的名稱,第二個是options,包括下列屬性: * session:Boolean。設置是否需要session,默認為true * successRedirect:String。設置當驗證成功時的跳轉鏈接 * failureRedirect:String。設置當驗證失敗時的跳轉鏈接 * failureFlash:Boolean or String。設置為Boolean時,express-flash將調用use()里設置的message。設置為String時將直接調用這里的信息。 * successFlash:Boolean or String。使用方法同上。 第三個參數是callback。注意如果使用了callback,那么驗證之后建立session和發出響應都應該由這個callback來做,passport中間件之后不應該再有其他中間件或callback。以下是代碼: ~~~ app.get('/login', function(req, res, next) { ? passport.authenticate('local', function(err, user, info) { ??? if (err) { return next(err); } ??? if (!user) { return res.redirect('/login'); } ??? req.logIn(user, function(err) { ????? if (err) { return next(err); } ????? return res.redirect('/users/' + user.username); ??? }); ? })(req, res, next); }); ~~~ ### HTTP request操作 注意上面的代碼里有個req.logIn(),它不是http模塊原生的方法,也不是express中的方法,而是passport加上的,passport擴展了HTTP request,添加了四種方法。 * logIn(user, options, callback):用login()也可以。作用是為登錄用戶初始化session。options可設置session為false,即不初始化session,默認為true。 * logOut():別名為logout()。作用是登出用戶,刪除該用戶session。不帶參數。 * isAuthenticated():不帶參數。作用是測試該用戶是否存在于session中(即是否已登錄)。若存在返回true。事實上這個比登錄驗證要用的更多,畢竟session通常會保留一段時間,在此期間判斷用戶是否已登錄用這個方法就行了。 * isUnauthenticated():不帶參數。和上面的作用相反。 ### 完整示例 基本上passport本地驗證的知識點就是這些,下面給出一個相對完整的示例,包括bcrypt的實現,這里借用了nodeclub中的方法,為實現它你需要自己配置hash: ~~~ var express = require('express'); var cookieParser = require('cookie-parser'); var session = require('express-session'); var passport = require('passport'); var LocalStrategy = require('passport-local').Strategy; //User模型需自己實現 var User = require('../models/User'); var bcrypt = require('bcrypt'); passport.serializeUser(function(user, done) { ? done(null, user.id); }); passport.deserializeUser(function(id, done) { ? User.findById(id, function(err, user) { ??? done(err, user); ? }); }); //這里的username可以改成前端表單對應的命名,如: // <form><input type="text" name="hehe">...</form> //則這里將所有的username改為hehe passport.use(new LocalStrategy({ usernameField: 'username' }, function(username, password, done) { ? //實現用戶名或郵箱登錄 ? //這里判斷提交上的username是否含有@,來決定查詢的字段是哪一個 ? var criteria = (username.indexOf('@') === -1) ? {username: username} : {email: username}; ? User.findOne(criteria, function(err, user) { ??? if (!user) return done(null, false, { message: '用戶名或郵箱 ' + username + ' 不存在'}); ??? bcompare(password, hash, function(err, isMatch) { ????? if (isMatch) { ??????? return done(null, user); ????? } else { ??????? return done(null, false, { message: '密碼不匹配' }); ????? } ??? }); ? }); })); ... app.use(cookieParser()); app.use(session({secret: "need change"})); app.use(passport.initialize()); app.use(passport.session()); app.use(flash()); ... app.post('/login', passport.authenticate('local', function(err, user, info) { ??? if (err) return next(err); ??? if (!user) { ????? req.flash('errors', { msg: info.message }); ????? return res.redirect('/login'); ??? } ??? req.logIn(user, function(err) { ????? if (err) return next(err); ????? req.flash('success', { msg: '登錄成功!' }); ????? res.redirect('/'); ??? }); ? })(req, res, next) ); //這里getUser方法需要自定義 app.get('/user', isAuthenticated, getUser); app.get('/logout', function(req, res){ ? req.logout(); ? res.redirect('/'); }); //將req.isAuthenticated()封裝成中間件 var isAuthenticated = function(req, res, next) { ? if (req.isAuthenticated()) return next(); ? res.redirect('/login'); }; var bcompare = function (str, hash, callback) { bcrypt.compare(str, hash, callback); }; ~~~ passport-local模塊也包括一些示例,不過這些示例都是Express 3.x時代寫的,所以不要原封不動的copy代碼。 基本的local驗證就講到這里,下面還有進階的驗證技巧,比如在RESTful API中使用passport,驗證多個條件等。 ## OAuth驗證 OAuth驗證是體現passport強大的地方,如果你看過nodeclub的源碼,會發現它自己實現了local驗證,但它的Github驗證是用passport來實現的。 OAuth標準分為兩個版本,1.0版和2.0版,兩者被使用的都很廣泛,passport通過passport-oauth為兩者提供支持,使用下面的命令可以安裝。 ~~~ npm install passport-oauth ~~~ ### OAuth驗證流程 OAuth1.0和2.0的使用流程都差不多,一般來說如下: 1. 為你的app去第三方服務商處申請標識和令牌appkey和secret; 2. 在你的app里添加按鈕或鏈接,將用戶引導至服務商的授權頁,用戶在這里選擇授權給你的app; 3. 授權成功后跳轉回你的app,同時還傳遞回access_token和一些用戶資料。 到這里首次驗證流程就完成了,之后只要拿access_token去就可以做登錄驗證或者其他事了。 ### OAuth1.0 要使用passport OAuth1.0驗證你需要先引入: ~~~ var passport = require('passport') ? , OAuthStrategy = require('passport-oauth').OAuthStrategy; ~~~ 然后是配置: ~~~ passport.use('provider', new OAuthStrategy({ ??? requestTokenURL: 'https://www.provider.com/oauth/request_token', ??? accessTokenURL: 'https://www.provider.com/oauth/access_token', ??? userAuthorizationURL: 'https://www.provider.com/oauth/authorize', ??? consumerKey: '123-456-789', ??? consumerSecret: 'shhh-its-a-secret' ??? callbackURL: 'https://www.example.com/auth/provider/callback' ? }, ? function(token, tokenSecret, profile, done) { ??? User.findOrCreate(..., function(err, user) { ????? done(err, user); ??? }); ? } )); ~~~ 這里比通用流程多的一點就是,你的App需要先訪問第三方服務,獲取request token,這個request token是未授權的,等用戶授權之后,可以拿這個request token去換取access token。 在passport中你不必管這些細節,找到第三服務的文檔找到對應的URL添上即可。當然你還得申請key和secret。 use方法的回調接受四個參數,token就是access token,和tokenSecret一起好好保存。profile則是用戶在第三方服務上的一些公開資料,它的模型在[這里](http://passportjs.org/guide/profile/),不過返回的資料不一定全面,在使用前需要驗證是否存在。 OAuth1.0的路由常見寫法如下: ~~~ app.get('/auth/provider', passport.authenticate('provider')); app.get('/auth/provider/callback', ? passport.authenticate('provider', { successRedirect: '/', ????????????????????????????????????? failureRedirect: '/login' })); ~~~ 上面就是OAuth1.0的驗證流程。 OAuth1.0主要是一些比較早提供第三方登錄功能的網站使用,現在的網站大部分使用OAuth2.0了,新浪微博原先使用的是1.0,現在也改用2.0了。 ### OAuth2.0 OAuth2.0的驗證不需要request_token,但比1.0多了scope和refresh token,我們先來看看具體的配置方法: ~~~ var passport = require('passport') ? , OAuth2Strategy = require('passport-oauth').OAuth2Strategy; passport.use('provider', new OAuth2Strategy({ ??? authorizationURL: 'https://www.provider.com/oauth2/authorize', ??? tokenURL: 'https://www.provider.com/oauth2/token', ??? clientID: '123-456-789', ??? clientSecret: 'shhh-its-a-secret' ??? callbackURL: 'https://www.example.com/auth/provider/callback' ? }, ? function(accessToken, refreshToken, profile, done) { ??? User.findOrCreate(..., function(err, user) { ????? done(err, user); ??? }); ? } )); ~~~ refreshToken是重新獲取access token的方法,因為access token是有使用期限的,到期了必須讓用戶重新授權才行,現在有了refresh token,你可以讓應用定期的用它去更新access token,這樣第三方服務就可以一直綁定了。不過這個方法并不是每個服務商都提供,注意看服務商的文檔。 下面是路由,OAuth2.0也有一點不同: ~~~ app.get('/auth/provider', ? passport.authenticate('provider', { scope: 'email' }) ); app.get('/auth/provider/callback', ? passport.authenticate('provider', { successRedirect: '/', ????????????????????????????????????? failureRedirect: '/login' })); ~~~ scope是權限范圍,需要在服務商處事先申請,想進一步了解可參考微博的[scope文檔](http://open.weibo.com/wiki/Scope)。它可以只有一項,也可以有多項,當為多項時以數組形式表示。 ### 使用passport-x插件 passport-oauth包含通用的驗證方法,基本山任何提供OAuth的服務都能用上面的方法來驗證,但大部分提供第三方登錄的網站都有passport的插件,它們的列表見[官網](http://passportjs.org/guide/providers/)和[Github wiki](https://github.com/jaredhanson/passport/wiki/Strategies#providers)。使用它們可以讓app綁定第三方服務更加簡單和模塊化。 passport-x插件的一般用法如下(以Github為例)。 首先安裝passport-github,注意這種情況不需要安裝passport-oauth: ~~~ npm install passport-github ~~~ 安裝完后是配置: ~~~ var passport = require('passport') ? , GithubStrategy = require('passport-github').Strategy; //passport設置部分 passport.use(new GithubStrategy({ ??? clientID: GITHUB_CLIENT_ID, ??? clientSecret: GITHUB_CLIENT_SECRET, ??? callbackURL: "http://www.example.com/auth/github/callback" ? }, ? function(accessToken, refreshToken, profile, done) { ??? User.findOrCreate(..., function(err, user) { ????? if (err) { return done(err); } ????? done(null, user); ??? }); ? } )); ... //路由部分 app.get('/auth/github', passport.authenticate('github')); app.get('/auth/github/callback', ? passport.authenticate('github', { failureRedirect: '/login' }), ? function(req, res) { ??? res.redirect('/'); ? }); ~~~ 與通用OAuth驗證流程對比,上面的代碼少了服務商的驗證頁部分,你只需要將獲得的appkey和secret填到對應地方即可。 ### OAuth驗證的邏輯 OAuth驗證的麻煩之處主要是處理邏輯,很多網站將第三方的OAuth作為一種用戶注冊手段,當用戶點擊第三方登錄時,若用戶未注冊會為他們創建賬號,這里面的邏輯就比較繞了。比如Hackathon Starter的處理邏輯如下: ~~~ /** ?* OAuth驗證策略概述 * ?* 當用戶點擊“使用XX登錄”鏈接 ?* - 若用戶已登錄 ?*?? - 檢查該用戶是否已綁定XX服務 ?*???? - 如果已綁定,返回錯誤(不允許賬戶合并) ?*???? - 否則開始驗證流程,為該用戶綁定XX服務 ?* - 用戶未登錄 ?*?? - 檢查是否老用戶 ?*???? - 如果是老用戶,則登錄 ?*???? - 否則檢查OAuth返回profile中的email,是否在用戶數據庫中存在 ?*?????? - 如果存在,返回錯誤信息 ?*?????? - 否則創建一個新賬號 ?*/ ~~~ 另外還有平常驗證用戶是否已綁定某個服務,可以封裝成中間件: ~~~ var isAuthorized = function(req, res, next) { ? if (req.user.provider)) { ??? next(); ? } else { ??? //do something else ? } }; ~~~ 學習OAuth驗證最好的項目是[Hackathon Starter](https://github.com/sahat/hackathon-starter),它實現了十幾種的第三方網站和服務的OAuth驗證,推薦學習。下面進階學習里面還有如何開發一個passport OAuth驗證插件。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看