国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JWT、OAuth 2.0、session 用戶授權實戰

zhaot / 940人閱讀

摘要:為用戶提供授權以允許用戶操作非公開資源,有很多種方式。具體的代碼根據不同的授權方案而有所不同。使用授權原理利用來驗證用戶,有兩種機制實現。使用來實現用戶授權主要用于簽發如果有將異步的簽名。


?

在很多應用中,我們都需要向服務端提供自己的身份憑證來獲得訪問一些非公開資源的授權。比如在一個博客平臺,我們要修改自己的博客,那么服務端要求我們能夠證明 “我是我” ,才會允許我們修改自己的博客。

為用戶提供授權以允許用戶操作非公開資源,有很多種方式。比如使用 token、session、cookie,還有允許第三方登錄授權的 OAuth 2.0.

為了理解這些技術的機制和它們之間的關系,本文就來一一使用這些方案實現一個前端通過后端驗證授權來訪問后端服務的應用。

我們將用 express 搭建一個簡單的后端,為了保存用戶信息,我們使用 mongoDB。前端是一個注冊頁面和一個登錄頁面,此外還有一個修改用戶密碼的頁面,在這個頁面上修改密碼的操作只有在用戶登錄之后才被允許,也就是被服務端授權之后才能修改密碼,否則返回 401 未授權。

下面就是我們這個簡單 demo 的文件結構:

服務端結構:

?

?

?

前端頁面結構:

?

?

?

如上圖,我們在服務端寫了4個路由分別用于用戶注冊、登錄、修改密碼、和登出。其中在登錄路由中,用戶登錄之后將會生成一個用戶憑證,在后續修改密碼的路由中將會利用這個憑證來授權用戶修改密碼。具體的代碼根據不同的授權方案而有所不同。前端相應地分為注冊、登錄、修改密碼 3 個頁面:

注冊頁面:

?

?

登錄頁面:

?

?

修改密碼頁面:

?

?

我們最終實現的效果就是: (GIF圖過大,可以轉到GitHub項目地址查看:地址)

搭建起一個前后端分離的應用框架之后,我們下面依次使用 token、OAuth 2.0、express-session 來實現用戶授權。

1. 使用 session 授權 1.1 session 原理:

利用 session 來驗證用戶,有兩種機制實現。


?

需要服務端在用戶登錄成功后生成一個 session ID 保存在服務端,這個session ID 標識當前會話的用戶,以后用戶的每一次請求中都會包含session ID,服務端可以識別這個 session ID 驗證用戶身份然后才會授權。


?

把 session ID 和其他數據加密后發給用戶,由用戶來存儲并在以后每次請求中發給服務端來驗證。比如可以用 cookie 存儲發送,也可以使用其他客戶端存儲。

1.2 express-session API:

本文使用 express-session 來實現。并且使用上述 session 的第一種機制。所以先來看一下 express-session 主要的 API:

?

session( options ):生成 session 中間件,使用這個中間件會在當前會話中創建 session,session 數據將會被保存在服務端,而 session ID 會保存在 cookie。options 為傳入的配置參數,有以下這些參數:

1. cookie: 存儲 session ID, 默認值 { path: ‘/‘, httpOnly: true,secure: false, maxAge: null }) 2. genid: 一個函數,返回一個字符串用來作為新的 session ID,傳入 req 可以按需在 req 上添加一些值。 3. name: 存儲 session ID 的 cookie 的名字,默認是"connect.sid",但是如果有多個使用 express-session 的 app 運行在同一個服務器主機上,需要用不同的名字命名 express-session 的 cookie。 4. proxy : 當設置了secure cookies(通過”x-forwarded-proto” header )時信任反向代理。 5. resave: 強制保存會話,即使會話在請求期間從未被修改過 6. rolling: 強制在每次響應時,都設置保存會話標識符的cookie。cookie 到期時間會被重置為原始時間 maxAge。默認值為`false`。 7. saveUninitialized: 默認 `true`, 強制存儲未初始化的 session。 8. secret ( 必需 ): 用來對session ID cookie簽名,可以提供一個多帶帶的字符串作為 secret,也可以提供一個字符串數組,此時只有第一個字符串才被用于簽名,但是在 express-session 驗證 session ID 的時候會考慮全部字符串。 9. store: 存儲 session 的實例。 10. unset: 控制 req.session 是否取消。默認是 `keep`,如果是 `destroy`,那么 session 就會在響應結束后被終止。

?

?

req.session:這是 express-session 存放 session 數據的地方,注意,只有 session ID 存儲在 cookie,所以 express-session 會自動檢查 cookie 中的 session ID ,并用這個 session ID 來映射到對應的 session 數據,所以使用 express-session 時我們只需讀取 req.session ,express-session 知道應該讀取哪個 session ID 標識的 session 數據。

1. 可以從 req.session 讀取 session : req.session.id:每一個 session 都有一個唯一ID來標識,可以讀取這個ID,而且只讀不可更改,這是 req.sessionID 的別名; req.session.cookie:每一個 session 都有一個唯一 的cookie來存儲 session ID,可以通過 req.session.cookie 來設置 cookie 的配置項,比如 req.session.cookie.expires 設置為 false ,設置 req.session.cookie.maxAge 為某個時間。 2. req.session 提供了這些方法來操作 session: req.session.regenerate( callback (err) ): 生成一個新的 session, 然后調用 callback; req.session.destroy( callback (err) ): 銷毀 session,然后調用 callback; req.session.reload( callback (err) ): 從 store 重載 session 并填充 req.session ,然后調用 callback; req.session.save( callback (err) ): 將 session 保存到 store,然后調用 callback。這個是在每次響應完之后自動調用的,如果 session 有被修改,那么 store 中將會保存新的 session; req.session.touch(): 用來更新 maxAge。

?

?

req.sessionID:和 req.session.id 一樣。

?

?

store:如果配置這個參數,可以將 session 存儲到 redis和mangodb 。一個使用 rtedis 存儲 session 的例子。store 提供了一下方法來操作 store:

1. store.all( callback (error, sessions) ) : 返回一個存儲store的數組; 2. store.destroy(sid, callback(error)): 用session ID 來銷毀 session; 3. store.clear(callback(error)): 刪除所有 session 4. store.length(callback(error, len)): 獲取 store 中所有的 session 的數目 5. store.get(sid, callbackcallback(error, session)): 根據所給的 ID 獲取一個 session 6. store.set(sid, session, callback(error)): 設置一個 session。 7. store.touch(sid, session, callback(error)): 更新一個 session

?

以上就是 express-session 的全部 API。

1.3 使用 express-session

重點中的重點,巨坑中的巨坑:使用 express-session 是依賴于 cookie 來存儲 session ID 的,而 session ID 用來唯一標識一個會話,如果要在一個會話中驗證當前會話的用戶,那么就要求用戶前端能夠發送 cookie,而且后端能夠接收 cookie。所以前端我們設置 axios 的 withCredentials = true 來設置 axios 可以發送 cookie,后端我們需要設置響應頭 Access-Control-Allow-Credentials:true,并且同時設置 Access-Control-Allow-Origin 為前端頁面的服務器地址,而不能是*。我們可以用 cors 中間件代替設置:

// 跨域 app.use(cors({ credentials: true, origin: "http://localhost:8082", // web前端服務器地址,,不能設置為 * }))

我開始就是因為沒有設置這個,所以遇到了問題,就是后端登錄接口在session中保存 用戶名( req.session.username = req.body.username) 之后,在修改用戶密碼的接口需要讀取 req.session.username 以驗證用戶的時候讀取不到 req.session.username ,很明顯兩個接口的 req.session 不是同一個 session,果然 console 出來 的 session ID 是不同的。這就讓我想到了 cookie,cookie 是生成之后每次請求都會帶上并且后端可以訪問的,現在存儲在 cookie 中的 session ID 沒有被讀取到而是讀取到了新 session ID,所以問題就出在后端不能拿到 cookie,也有可能是因為前端發送不出去 cookie。可是開始的時候搜索關于 session ID 讀取不一致的這個問題我找不到解決辦法,而且發現很多人存在同樣的問題,但是沒有人給出答案,現在通過自己的思考想到了解決辦法,這是很多人需要避免的巨坑。

現在跨過了最大的一個坑,我們就可以來編寫前后端所有的邏輯了。關于注冊的邏輯,是一個很簡單的用戶注冊信息填寫頁面,它發送用戶的名字和密碼到后端注冊接口,后端注冊接口保存用戶的名字和密碼到數據庫理。因此我在這里省略掉前端注冊頁面和后端注冊接口,只講前端登錄頁面和后端登錄接口,前端修改密碼頁面和后端修改密碼接口和登出接口。

?

前端登錄接口:

?

async function login(){ // 登錄 let res = await axios.post("http://localhost:3002/login",{username,password}) if(res.data.code === 0){ setLoginSeccess(true) alert("登錄成功,請修改密碼") }else if(res.data.code === 2){ alert("密碼不正確") return }else if(res.data.code === 1){ alert("沒有該用戶") return } }

?

后端登錄接口:

?

const getModel = require("../db").getModel const router = require("express").Router() const users = getModel("users") router.post("/", (req,res,next)=>{ let {username, password} = req.body users.findOne({username},(err,olduser)=>{ if(!olduser){ res.send({code:1})// 沒有該用戶 }else{ if(olduser.password === password){// 登陸成功,生成 session req.session.username = olduser.username req.session.userID = olduser._id console.log("登錄時的會話 ID:",req.sessionID) req.session.save() res.send({code:0})// 登錄成功 }else{ res.send({code:2}) // 密碼錯誤 } } }) }) module.exports = router

?

前端修改密碼和登出頁面:

?

// src/axios.config.js: // 支持 express-session 的 axios 配置 export function axios_session(){ axios.defaults.withCredentials = true return axios }

async function modify(){ // 修改密碼 if(!input.current.value) return alert("請輸入新密碼") try{ // 支持 session 的 axios 調用 let res = await axios_session().post("http://localhost:3002/modify",{newPassword:input.current.value}) if(res.data.code === 0) alert("密碼修改成功") }catch(err){ alert("沒有授權 401") console.log(err) } }

async function logout(){ // 登出 let res = await axios.post("http://localhost:3002/logout") if(res.data.code === 0){ history.back() } }

?

后端修改密碼接口:

?

const getModel = require("../db").getModel const router = require("express").Router() const users = getModel("users") const sessionAuth = require("../middlewere/sessionAuth") router.post("/", sessionAuth, (req,res,next)=>{ let {newPassword} = req.body console.log("修改密碼時的會話 ID:",req.session.id) if(req.session.username){ users.findOne({username: req.session.username},(err,olduser)=>{ olduser.password = newPassword olduser.save(err=>{ if(!err){ res.send({code:0})// 修改密碼成功 } }) }) } }) module.exports = router

sessionAuth 驗證中間件:

const sessionAuth = (req,res,next)=>{ if(req.session && req.session.username){// 驗證用戶成功則進入下一個中間件來修改密碼 next() }else{// 驗證失敗返回 401 res.sendStatus(401) } } module.exports = sessionAuth

?

后端登出:

?

const router = require("express").Router() router.post("/", (req,res,next)=>{ req.session.destroy(()=>console.log("銷毀session,已經推出登錄")) res.send({code:0}) }) module.exports = router

我們還需要調用 session 的中間件,配置一些參數,才能在之后的中間件中使用 req.session 來進行存儲、讀取和銷毀 session 的操作:

// server/app.js: // session app.use(session({ secret: "123456789",// 必需,用來簽名 session unset:"destroy",// 在每次會話就熟后銷毀 session resave:true, saveUninitialized:false, rolling:true, cookie:{ maxAge:60*60*1000// session ID 有效時間 } }))

2. 使用 JWT 授權 2.1 JWT 的原理:

首先來看看 JWT 的概念,JWT 的 token 由 頭部(head)、數據(payload)、簽名(signature) 3個部分組成 具體每個部分的結構組成以及JWT更深的講解可以看看這個。其中頭部(header)和數據(payload)經過 base64 編碼后經過秘鑰 secret的簽名,就生成了第三部分----簽名(signature) ,最后將 base64 編碼的 header 和 payload 以及 signature 這3個部分用圓點 . 連接起來就生成了最終的 token。

signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) token = base64UrlEncode(header) + "." + base64UrlEncode(payload) + signature

token 生成之后,可以將其發送給客戶端,由客戶端來存儲并在以后每次請求中發送會后端用于驗證用戶。前端存儲和發送 token 的方式有以下兩種:

2.1.1 使用 Header.Authorization + localStorage 存儲和發送 token

在 localStorage 中存儲 token,通過請求頭 Header 的 Authorization 字段將 token發送給后端。

這種方法可以避免 CSRF 攻擊,因為沒有使用 cookie ,在 cookie 中沒有 token,而 CSRF 就是基于 cookie 來攻擊的。雖然沒有 CSRF ,但是這種方法容易被 XSS 攻擊,因為 XSS 可以攻擊 localStorage ,從中讀取到 token,如果 token 中的 head 和 payload 部分沒有加密,那么攻擊者只要將 head 和 payload 的 base64 形式解碼出來就可以看到head 和payload 的明文了。這個時候,如果 payload 保護敏感信息,我們可以加密 payload。

2.1.2 使用 cookie 存儲和發送 token:

在這種情況下,我們需要使用 httpOnly 來使客戶端腳本無法訪問到 cookie,才能保證 token 安全。這樣就避免了 CSRF 攻擊。

2.2 使用 jsonwebtoken 來實現 JWT 用戶授權:

jsonwebtoken 主要 API:

1. jwt.sign(payload, secretOrPrivateKey, [options, callback]) 用于簽發 token

如果有 callback 將異步的簽名 token。

payload 就是我們要在 token 上裝載的數據,比如我們可以在上面添加用戶ID,用于數據庫查詢。payload可以是一個object, buffer或者string,payload 如果是 object,可以在里面設置 exp 過期時間。

secretOrPrivateKey 即包含HMAC算法的密鑰或RSA和ECDSA的PEM編碼私鑰的string或buffer,是我們用于簽名 token 的密鑰,secretOrPublicKey 應該和下面 的 jwt.verify 的 secretOrPublicKey 一致。

options 的參數有:

1)algorithm (default: HS256) 簽名算法,這個算法和下面將要講的 jwt.verify 所用的算法一個一致 2)expiresIn: 以秒表示或描述時間跨度zeit / ms的字符串。如60,"2 days","10h","7d",含義是:過期時間 3)notBefore: 以秒表示或描述時間跨度zeit / ms的字符串。如:60,"2days","10h","7d" 4)audience:Audience,觀眾 5)issuer: Issuer,發行者 6)jwtid: JWT ID 7)subject: Subject,主題 8)noTimestamp: 9)header 10)keyid 11)mutatePayload

2. jwt.verify(token, secretOrPublicKey, [options, callback]) 用于驗證 token

如果有 callback 將異步的驗證 token。

token 便是我們保存在前端的token,我們將它發送給后端,后端調用 jwt.verify 并接受 token 和傳入放在后端的 secretOrPublicKey 來驗證 token。注意這里的 secretOrPublicKey 與之前用于簽發 token 的 secretOrPublicKey 應該是同一個。

options 的參數有:

1)algorithms: 一個包含簽名算法的數組,比如 ["HS256", "HS384"]. 2)audience: if you want to check audience (aud), provide a value here. The audience can be checked against a string, a regular expression or a list of strings and/or regular expressions. Eg: "urn:foo", /urn:f[o]{2}/, [/urn:f[o]{2}/, "urn:bar"] 3)complete: return an object with the decoded { payload, header, signature } instead of only the usual content of the payload. 4)issuer (optional): string or array of strings of valid values for the iss field. 5)ignoreExpiration: if true do not validate the expiration of the token. 6)ignoreNotBefore... 7)subject: if you want to check subject (sub), provide a value here 8)clockTolerance: number of seconds to tolerate when checking the nbf and exp claims, to deal with small clock differences among different servers 9)maxAge: the maximum allowed age for tokens to still be valid. It is expressed in seconds or a string describing a time span zeit/ms. Eg: 1000, "2 days", "10h", "7d". A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc), otherwise milliseconds unit is used by default ("120" is equal to "120ms"). 10)clockTimestamp: the time in seconds that should be used as the current time for all necessary comparisons. 11)nonce: if you want to check nonce claim, provide a string value here. It is used on Open ID for the ID Tokens. (Open ID implementation notes)

3. jwt.decode(token [, options]) 解碼 token

只是解碼 token 中的 payload,不會驗證 token。 options 參數有:

1)json: 強制在 payload 用JSON.parse 序列化,即使頭部沒有聲明 "typ":"JWT" 2)complete: true 則返回解碼后的包含 payload 和 header 的對象.

4. 錯誤碼

在驗證 token 的過程中可能或拋出錯誤,jwt.verify() 的回調的第一個參數就是 err,err 對象有一下幾種類型:

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/6742.html

相關文章

  • JWTOAuth 2.0session 用戶授權實戰

    摘要:為用戶提供授權以允許用戶操作非公開資源,有很多種方式。具體的代碼根據不同的授權方案而有所不同。使用授權原理利用來驗證用戶,有兩種機制實現。使用來實現用戶授權主要用于簽發如果有將異步的簽名。注意這里的與之前用于簽發的應該是同一個。 在很多應用中,我們都需要向服務端提供自己的身份憑證來獲得訪問一些非公開資源的授權。比如在一個博客平臺,我們要修改自己的博客,那么服務端要求我們能夠證明 我是...

    Jochen 評論0 收藏0
  • Spring Security

    摘要:框架具有輕便,開源的優點,所以本譯見構建用戶管理微服務五使用令牌和來實現身份驗證往期譯見系列文章在賬號分享中持續連載,敬請查看在往期譯見系列的文章中,我們已經建立了業務邏輯數據訪問層和前端控制器但是忽略了對身份進行驗證。 重拾后端之Spring Boot(四):使用JWT和Spring Security保護REST API 重拾后端之Spring Boot(一):REST API的搭建...

    keelii 評論0 收藏0
  • 聊聊鑒權那些事

    摘要:什么是鑒權鑒權是指驗證用戶是否擁有訪問系統的權利。傳統的鑒權是通過密碼來驗證的。這種方式的前提是,每個獲得密碼的用戶都已經被授權。接下來就一一介紹一下這三種鑒權方式。 在系統級項目開發時常常會遇到一個問題就是鑒權,身為一個前端來說可能我們距離鑒權可能比較遠,一般來說我們也只是去應用,并沒有對權限這一部分進行深入的理解。 什么是鑒權 鑒權:是指驗證用戶是否擁有訪問系統的權利。傳統的鑒權是...

    wslongchen 評論0 收藏0
  • 使用 OAuth 2 和 JWT 為微服務提供安全保障

    摘要:作為目前最主流的微服務框架,發展速度很快,成為了最全面的微服務解決方案。通過認證后,轉發給內部相應的服務器。所有遠程訪問資源服務器相關的必須提供。 Part 1 - 理論相關 作者 freewolf 關鍵詞 微服務、Spring Cloud、OAuth 2.0、JWT、Spring Security、SSO、UAA 寫在前面 作為從業了十多年的IT行業和程序的老司機,今天如果你說你不懂...

    littleGrow 評論0 收藏0
  • 使用 OAuth 2 和 JWT 為微服務提供安全保障

    摘要:作為目前最主流的微服務框架,發展速度很快,成為了最全面的微服務解決方案。通過認證后,轉發給內部相應的服務器。所有遠程訪問資源服務器相關的必須提供。 Part 1 - 理論相關 作者 freewolf 關鍵詞 微服務、Spring Cloud、OAuth 2.0、JWT、Spring Security、SSO、UAA 寫在前面 作為從業了十多年的IT行業和程序的老司機,今天如果你說你不懂...

    xzavier 評論0 收藏0

發表評論

0條評論

zhaot

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<