[譯]Passport.js 文檔——一般原則

blank

[譯]Passport.js 文檔——一般原則

Overview概覽

Passport 是一個Node 平台的身份認證中間件。它的設計服務於一個簡單的目標:身份認證請求。編寫模塊時,封裝是一個美德,所以Passport 把所有其他功能交給應用自身去做。這種關注點的分離可以保持代碼整潔並且可維護,同時使得Passport 極其容易地集成到應用中。

在現代web 應用中,身份認證可以採用多種形式。傳統而言,用戶通過提供用戶名和密碼登錄。由於社交網絡的興起,使用類似於Facebook 或者Twitter 的OAuth 提供商進行單點登錄(single sign-on)已經成為一個流行的身份驗證方式。暴露API 的服務通常需要基於token 的憑證來保護訪問行為。

Passport 認為每一個應用都有獨一無二的身份認證需求。身份認證機制,被稱為策略(strategies),打包成獨立的模塊。應用可以選擇應用哪些策略,同時避免創建不必要的依賴。

儘管身份驗證過程很複雜,代碼也可以很簡潔。

app.post('/login',passport.authenticate('local',{successRedirect:'/',failureRedirect:'/login'}));

Authenticate身份認證

認證請求很簡單,只需要調用passport.authenticate()並且指定要應用的策略。 authenticate的函數簽名是一個標準的Connect中間件,因此可以方便地作為Express應用中的路由中間件。

app . post ( '/login' , passport . authenticate ( 'local' ), function ( req , res ) { // 如果这个函数被调用了,说明认证成功。 // `req.user` 包含已认证的用户res . redirect ( '/users/' + req . user . username ); });

默認情況下,如果認證失敗,Passport將響應一個401 Unauthorized狀態,並且任何額外的路由處理器都不會被調用。如果認證成功,下一個處理器會被調用,同時req.user屬性將被設置為已認證的用戶。

注意:在路由中使用之前,策略必須被提前設置好。

Redirects重定向

認證一個請求後通常會發出一個重定向。

app.post('/login',passport.authenticate('local',{successRedirect:'/',failureRedirect:'/login'}));

在這種情況下,重定向選項覆蓋了默認的行為。一旦認證成功,用戶將被重定向到首頁。如果認證失敗,用戶將被重定向回登陸頁進行重試。

Flash Messages快閃消息

重定向經常和快閃消息(Flash Messages) 結合,用來展示向用戶展示狀態訊息。

app.post('/login',passport.authenticate('local',{successRedirect:'/',failureRedirect:'/login',failureFlash:true}));

設置failureFlash選項為true通知Passport快速展示一個error消息,如果有的話,策略驗證回調函數提供的消息。這通常是最佳的方式,因為驗證回調函數可以最準確地確認為何認證失敗。

另外,快閃消息可以被顯式地設置。

passport.authenticate('local',{failureFlash:'Invalid username or password.'});

successFlesh選項可以用來在認證成功時顯示一個success消息。

passport.authenticate('local',{successFlash:'Welcome!'});

Disable Sessions禁用會話

成功認證之後,Passport 將建立一個持續的登錄會話。這在用戶通過瀏覽器訪問一個web 應用的普遍場景中是有用的。然而,在某些情況下,會話支持是不必要的。例如,API 服務通常需要每一個請求都提供一個憑證。在這種情況下,會話支持可以通過設置session選項為fasle來被安全地禁用。

app.get('/api/users/me',passport.authenticate('basic',{session:false}),function(req,res){res.json({id:req.user.id,username:req.user.username});});

Custom Callback自定義回調

如果內置的選項不足以處理身份認證請求,可以提供一個自定義的回調函數來允許應用自己處理成功或者失敗。

app.get('/login',function(req,res,next){passport.authenticate('local',function(err,user,info){if(err){returnnext(err);}if(!user){returnres.redirect('/login');}req.logIn(user,function(err){if(err){returnnext(err);}returnres.redirect('/users/'+user.username);});})(req,res,next);});

在這個例子中,注意authenticate實在路由處理器內部被調用的,而不是作為一個路由中間件。通過閉包來使回調函數訪問到reqres對象。

如果身份認證失敗, user將被設置為false 。如果一個異常發生, err將被設置。一個可選的info參數將被傳入,包含這個策略的驗證回調函數提供的其他細節。

回調函數可以根據需要使用提供的參數來處理認證的結果。注意在使用自定義回調函數時,應用需要負責建立回話(通過調用req.login())和發送響應。

Configure配置

使用Passport 進行身份驗證,有三個部分需要設置:

  1. 認證策略
  2. 應用中間件
  3. 會話(可選)

Strategies策略

Passport 是使用所謂策略(strategies) 來對請求進行身份認證。策略的範圍包括驗證用戶名和密碼,使用OAuth 的委託認證,或者使用OpenID 的聯合認證。

在請求Passport 對一個請求進行身份認證前,必需配置應用所使用的策略。

策略和他們的配置,通過use函數提供。例如,下面為用戶名/密碼認證使用了LocalStrategy

app.get('/login',function(req,res,next){passport.authenticate('local',function(err,user,info){if(err){returnnext(err);}if(!user){returnres.redirect('/login');}req.logIn(user,function(err){if(err){returnnext(err);}returnres.redirect('/users/'+user.username);});})(req,res,next);});

Verify Callback驗證回調函數

這個例子介紹了一個重要的概念。策略需要所謂驗證回調函數。驗證回調函數的目的是找到擁有一組憑證的用戶。

當Passport 對一個請求進行身份認證時,它解析包含在請求中的憑證。然後將這些憑證作為參數調用驗證回調函數,在這個例子中是usernamepassword 。如果憑證有效,驗證回調調用done來把這個認證了的用戶提供給Passport。

returndone(null,user);

如果憑證無效(例如,密碼不正確),應該在調用done時傳入false而不是一個用戶來告訴Passport一個身份認證失敗。

returndone(null,false);

可以提供一個額外的訊息消息來說明失敗的原因。這有助於顯示一個快閃消息提示用戶重試時。

returndone(null,false,{message:'Incorrect password.'});

最後,在驗證憑證時,如果發生一個異常(例如,數據庫不可用),應該按照常規的Node風格,在調用done時傳入一個錯誤。

returndone(err);

注意區分這兩種失敗情況是很重要。後者是一個服務器異常, err是一個非null值。身份認證失敗是自然的情況,服務器運行正常。確保err保持null ,同時使用最後的參數來傳遞額外的細節。

通過這種方式的委託,驗證回調函數保持Passport 獨立於數據庫。身份認證層不會強加任何前提條件,應用可以自由地選擇如何存儲用戶訊息。

Middleware中間件

在一個基於Connect或Express的應用中,需要使用passport.initailize()中間件來初始化Passport。如果你的應用使用持續的登錄會話, passport.sesson()中間件也必須使用。

app.configure(function(){app.use(express.static('public'));app.use(express.cookieParser());app.use(express.bodyParser());app.use(express.session({secret:'keyboard cat'}));app.use(passport.initialize());app.use(passport.session());app.use(app.router);});

注意啟用會話支持是完全可選的,雖然在大多數應用中推薦使用它。如果啟用,確保在passport.session之前使用session()來保證登錄回話按照正確的順序保存。

在Express 4.x中Connect中間件不在包含在Express核心中, app.configure()方法被移除。相同的中間件可以在與其等價的npm 模塊中找到。

varsession=require("express-session"),bodyParser=require("body-parser");app.use(express.static("public"));app.use(session({secret:"cats"}));app.use(bodyParser.urlencoded({extended:false}));app.use(passport.initialize());app.use(passport.session());

Sessions會話

在一個典型web 應用中,用來認證一個用戶的憑證只在登錄請求時傳遞。如果登錄成功,一個會話將被建立同時通過用戶瀏覽器的一個cookie 設置來維護。

隨後的每一個請求將不再包含憑證,但是帶有標識回話的唯一cookie。為了支持登錄回話,Passport會將user實例序列化到回話並從回話中反序列化user實例。

passport.serializeUser(function(user,done){done(null,user.id);});passport.deserializeUser(function(id,done){User.findById(id,function(err,user){done(err,user);});});

在這個例子中,只有用戶的ID 被序列化到會話中,保持了會話中存儲最少的數據。接受到隨後的請求時,這個ID被用來查找用戶,用戶將被存儲到req.user

這個序列化和反序列化的邏輯是由應用提供的,允許應用不受身份認證的限制,選擇一個合適的數據庫或對象映射器。

Username & Password用戶和密碼

在網站中使用範圍最廣的用戶身份認證方式是通過一個用戶名和密碼。對這種機制的支持由passport-local模塊提供。

Configuration配置

varpassport=require('passport'),LocalStrategy=require('passport-local').Strategy;passport.use(newLocalStrategy(function(username,password,done){User.findOne({username:username},function(err,user){if(err){returndone(err);}if(!user){returndone(null,false,{message:'Incorrect username.'});}if(!user.validPassword(password)){returndone(null,false,{message:'Incorrect password.'});}returndone(null,user);});}));

本地身份認證的驗證回調接受usernamepassword參數,通過登錄表單提交給應用。

Form表單

表單放置在web 頁面上,允許用戶輸入他們的憑證然後登錄。

<formaction="/login"method="post"><div><label>Username:</label><inputtype="text"name="username"/></div><div><label>Password:</label><inputtype="password"name="password"/></div><div><inputtype="submit"value="Log In"/></div></form>

Route路由

登錄表單通過POST提交給服務器。使用authenticate()local策略可以處理這個登錄請求。

app.post('/login',passport.authenticate('local',{successRedirect:'/',failureRedirect:'/login',failureFlash:true}));

設置failureFlash選項為true命令Passport使用上面的驗證回調設置的message選項來快速展示一個error消息。這有助於提示用戶重試。

Parameters參數

默認地, LocalStrategy期望使用名為usernamepassword的參數中查找憑證。如果你的網站想要給這些字段重新命名,可以通過選項來改變默認值。

passport . use ( new LocalStrategy ({ usernameField : 'email' , passwordField : 'passwd' }, function ( username , password , done ) { // ... } ));

OpenID

OpenID是一個聯合認證的開放標準。當訪問一個網站時,用於提交他們的OpenID 來登錄。然後用戶使用他們選擇的OpenID 提供者進行身份認證,提供者發出一個聲明來確認這個用戶的身份。網站驗證這個聲明來登記用戶。

對OpenID的支持由passport-openid模塊提供。

Configuration配置

使用OpenID 時,需要指定一個返回URL 和範圍。 returnURL是用戶在他們的OpenID提供者認證後重定向的目標。 realm指示了身份認證生效的URL空間部分。通常它是網站的根URL。

varpassport=require('passport'),OpenIDStrategy=require('passport-openid').Strategy;passport.use(newOpenIDStrategy({returnURL:'http://www.example.com/auth/openid/return',realm:'http://www.example.com/'},function(identifier,done){User.findOrCreate({openId:identifier},function(err,user){done(err,user);});}));

OpenID認證的驗證回調函數接受一個identifier參數,包含了用戶的身份標識。

Form表單

表單位於web 頁面,允許用戶輸入他們的OpenID 然後登錄。

<formaction="/auth/openid"method="post"><div><label>OpenID:</label><inputtype="text"name="openid_identifier"/><br/></div><div><inputtype="submit"value="Sign In"/></div></form>

Routes路由

OpenID 認證需要兩個路由。第一個路由接受表單提交,包含OpenID 身份標識。在認證過程中,用戶將被重定向到他們的OpenID 提供商。第二個路由是用戶與OpenID 提供商認證後返回的URL。

// 接受OpenID 身份标识,然后将用户重定向到他们的OpenID 提供商进行认证。 // 完成后,提供商会把用户定向到应用的地址: // /auth/openid/return app . post ( '/auth/openid' , passport . authenticate ( 'openid' )); // OpenID 提供商已经将用户重定向到应用。通过校验这个声明来完成认证过程。 // 如果有效,这个用户将会登录。否则,认证失败。 app . get ( '/auth/openid/return' , passport . authenticate ( 'openid' , { successRedirect : '/' , failureRedirect : '/login' }));

Profile Exchange

可以選擇性地配置OpenID 來檢索被認證用戶訊息。通過設置profile選項為true來啟用用戶訊息交換。

passport . use ( new OpenIDStrategy ({ returnURL : 'http://www.example.com/auth/openid/return' , realm : 'http://www.example.com/' , profile : true }, function ( identifier , profile , done ) { // ... } ));

用戶訊息交換被啟用時,驗證回調函數的簽名接受一個額外的profile參數,包含OpenID提供商提供的用戶訊息。

OAuth

OAuth是一個允許用戶授權web和桌面或移動應用進行API訪問的標準協議。一旦訪問被批准,被授權應用可以以這個用戶的名義使用API。 OAuth也成為了一個流行的委託認證機制。

OAuth 有兩種風格,都被廣泛部署。

初始版本的OAuth 作為一個開放標準,由一個鬆散的web 開發者組織開發。他們的工作最終成為OAuth 1.0 ,後來被OAuth 1.0a替代。這項工作成果已被IETF標準化為RFC 5849

Web Authorization Protocol Working Group接手的新的工作計劃,已經致力於定義OAuth 2.0 。鑑於漫長的標準化工作計劃,服務提供者已經開始部署符合多個草案的實現,包含了輕微的語義差別。

幸好,Passport 使應用規避了處理OAuth 變體的複雜性。在很多情況下,可以使用一個提供者相關的策略,而不是上述的通用OAuth 策略。這減少了一些必要設置,並能快速適配任何提供商特定的怪異模式。

對OAuth的支持由passport-oauth模塊提供。

OAuth 1.0

OAuth 1.0 是一個包含了多個步驟的委託認證策略。第一步,獲取一個request token。然後,用戶被重定向到服務提供商進行訪問授權。最後,授權被批准後,用戶被定向回應用,同時可以用request token 換取access token。請求訪問的應用,被稱為消費者(consumer),通過一個consumer key 和consumer secret 標識。

Configuration配置

使用通用的OAuth 策略時,key, secret 和終端通過選項指定。

varpassport=require('passport'),OAuthStrategy=require('passport-oauth').OAuthStrategy;passport.use('provider',newOAuthStrategy({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);});}));

基於OAuth的策略的驗證回調函數接受token , tokenSecretprofile參數。 token是access token , tokenSecret是它對應的secret。 profile將包含服務提供商提供的用戶訊息。

Routes路由

OAuth 認證需要兩個路由。第一個路由創建一個OAuth 事務並將用戶定向到服務提供商。第二個路由是用戶與提供商認證後返回的URL。

// 将用户重定向到OAuth 提供商进行认证。完成后,提供商将用户定向回应用的地址: // /auth/provider/callback app . get ( '/auth/provider' , passport . authenticate ( 'provider' )); // OAuth 提供商已经将用户重定向回应用。 // 试图获取access token 来完成认证。如果授权被许可,用户将登录。否则,认证失败app . get ( '/auth/provider/callback' , passport . authenticate ( 'provider' , { successRedirect : '/' , failureRedirect : '/login' }));

Link鏈接

鏈接或者按鈕可以放置在web 頁面中,點擊即開始認證過程。

<ahref="/auth/provider">Log In with OAuth Provider</a>

OAuth 2.0

OAuth 2.0 是OAuth 1.0 的繼任者,為解決早期版本中的一些已知的缺陷而設計。認證的過程基本相同。用戶先被重定向到服務提供商進行訪問授權。授權被許可後,用戶被重定向回到應用,攜帶一個用於交換access token 的code。請求訪問的應用,被稱為client,通過ID 和secret 標識。

varpassport=require('passport'),OAuth2Strategy=require('passport-oauth').OAuth2Strategy;passport.use('provider',newOAuth2Strategy({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);});}));

基於OAuth 2.0的策略的驗證回調函數接受accessTokenrefreshTOkenprofile參數。 refreshToken可以用來獲取新的access token,如果提供商沒有發送refresh token時可能為undefinedprofile將包含服務提供商提供的用戶訊息。

Routes路由

OAuth 2.0 認證需要兩個路由。第一個路由將用戶重定向到服務提供商。第二個路由是用戶與提供商認證結束後重定向的URL。

// 将用户重定向到OAuth2.0 提供商进行认证。 // 完成后,提供商将用户重定向回到应用的地址: // /auth/provider/callback app . get ( '/auth/provider' , passport . authenticate ( 'provider' )); // OAuth 2.0 提供商已经将用户重定向回到应用。 // 尝试获取access token 来完成认证过程。如果授权被许可,用户将成功登录。否则,认证失败。 app . get ( '/auth/provider/callback' , passport . authenticate ( 'provider' , { successRedirect : '/' , failureRedirect : '/login' }));

Scope作用域

使用OAuth 2.0 請求訪問時,反問的作用域通過scope 選項控制。

app.get('/auth/provider',passport.authenticate('provider',{scope:'email'}));Multiplescopescanbespecifiedasanarray.app.get('/auth/provider',passport.authenticate('provider',{scope:['email','sms']}));

scope 選項的值是提供商相關的。查閱提供商的文檔來獲取關於所支持的作用域的細節。

Link鏈接

鏈接或者按鈕可以放置到web 頁面中,點擊即開始認證過程。

<ahref="/auth/provider">Log In with OAuth 2.0 Provider</a>

User Profile用戶訊息

當使用類似於Facebook 或Twitter 的第三方服務進行認證時,用戶訊息通常是可以訪問的。每一個服務往往使用一個不一樣的方式來編碼這個訊息。為了方便集成,Passport 最大程度地標準化了用戶訊息。

標準化後的用戶訊息遵循Joseph Smarr定制的contact schema 。可用的通用字段可以概括為下表。

provider {String}用戶認證的提供商( facebook , twitter , etc.)

id {String}一個唯一的用戶標識,由服務提供商生成。

displayName {String}這個用戶適合展示的名字。

name {Object}

  • familyName {String}這個用戶的姓
  • givenName {String}這個用戶的名
  • middleName {String}這個用戶的中間名

emails {Array} [n]

  • value {String}實際的email地址
  • type {String} email地址的類型(home, work, etc.)

photos {Array} [n]

  • value {String}圖片的URL

注意所有上述字段不是對每一個服務提供商都可用。有些提供商可能包含額外的沒有在這裡描述的字段。查閱服務商相關的文檔獲取更多細節。

What do you think?

Written by marketer

blank

從源碼全面剖析React 組件更新機制

blank

為Node.js 應用建立一個更安全的沙箱環境