<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
伺服器端渲染簡單來說就是前端頁面是由伺服器通過字串拼接動態生成的,使用者端不需要額外通過Ajax請求引數,只需要做好渲染工作即可。
對於伺服器端渲染,推薦使用Session認證機制,再次之前,先說明一下cookie
比如你可以在baidu.com看到以下cookie:
session的鑑權就是利用了cookie,使用者呼叫登入介面,完成賬號密碼的校驗之後,將使用者資訊或者其他校驗資訊生成為cookie字串,返回給使用者,同時將cookie儲存在伺服器記憶體,使用者請求其他介面時,會在請求頭自動將cookie傳送給伺服器,伺服器會通過與伺服器記憶體中的使用者資訊匹配,如果匹配成功,則返回使用者端想要的內容,否則丟擲錯誤提示使用者端需要重新登入。大致流程圖如下:
接下來我們來進行實踐操作,在此之前請預先執行 npm i express express-session
,安裝所需要的模組。
index.js檔案的程式碼如下:
// 匯入 express 模組 const express = require('express') // 建立 express 的伺服器範例 const app = express() // 01:設定 Session 中介軟體 const session = require('express-session') app.use( session({ secret: 'heyyyyfx',//此處的secret金鑰可以是任意字串,是你自己制定的專屬加密方案,此處筆者將以自己的名字為例 resave: false,//無需在意,但是要寫上 saveUninitialized: true,//無需在意,但是要寫上 }) ) // 託管靜態頁面,此處筆者代理了一個靜態檔案,檔案內容下文可見。 app.use(express.static('./pages')) // 解析 POST 提交過來的表單資料 app.use(express.urlencoded({ extended: false })) // 登入的 API 介面 app.post('/api/login', (req, res) => { // 判斷使用者提交的登入資訊是否正確,此處寫死一個賬號密碼校驗,在實際開發中肯定是需要資料庫匹配。 if (req.body.username !== 'admin' || req.body.password !== '000000') { return res.send({ status: 1, msg: '登入失敗' }) } // 02:請將登入成功後的使用者資訊,儲存到 Session 中 // 注意:只有成功設定了 express-session 這個中介軟體之後,才能夠通過 req 點出來 session 這個屬性 req.session.user = req.body // 使用者的資訊,我們將使用者的資訊轉換成cookie字串返回給使用者。 req.session.islogin = true // 使用者的登入狀態,也是我們鑑權的參考 res.send({ status: 0, msg: '登入成功' }) }) // 獲取使用者姓名的介面 app.get('/api/username', (req, res) => { // 03:請從 Session 中獲取使用者的名稱,響應給使用者端 if (!req.session.islogin) {//此處就進行了鑑權,看使用者的cookie是否有我們之前傳送給他的islogin欄位。 return res.send({ status: 1, msg: 'fail' }) } res.send({ status: 0, msg: 'success', username: req.session.user.username, }) }) // 退出登入的介面 app.post('/api/logout', (req, res) => { // 04:清空 Session 資訊 req.session.destroy() res.send({ status: 0, msg: '退出登入成功', }) }) // 呼叫 app.listen 方法,指定埠號並啟動web伺服器 app.listen(80, function () { console.log('Express server running at http://127.0.0.1:80') })
筆者在此只附上index.js的內容,其他檔案內容可以在文末中拿到原始碼。
可以看到,Session機制需要cookie的配合才能實現,因此cookie的的缺點或特性也就會影響到Session鑑權,比如,cookie是預設不支援跨域的,當前端跨域請求後端介面時,需要做很多額外的設定,這也就是為什麼Session推薦在伺服器端使用。
筆者在本文中說到的的跨域問題,指的是使用者端和伺服器端二者的跨域,如果讀者下載了原始碼,可以看到筆者是在app.js(index.js)中使用app.use(express.static('./pages'))
進行了靜態託管,以此來保證使用者端和伺服器端都是locallhost:80,是同源的。感興趣的讀者可以嘗試用live Sever來代理Index.html檔案,看看效果如何,在此之前記得引入cors
中介軟體支援跨域。
其實筆者在此只是簡單講解了Session鑑權的大致原理以及進行了簡單的實現,在實際真實開發中,首先我們不建議將使用者資訊返回生成cookie字串再返回給使用者端,因為這是非常隱私的資訊,其次要知道cookie是可以直接在使用者端更改的,因此鑑權關鍵欄位也是需要斟酌的,現實開發是非常嚴謹的,請讀者在實際使用時秉承嚴謹的態度。
上文已經說到了,session會受到跨域的影響,因此在前後端分離開發以及存在跨域的情況下,我們推薦使用JWT鑑權。
JWT原理和Session大致相同,不同的點在於,JWT生成的Token字串需要使用者端手動儲存在localStorage或sessionStorage中。再次請求時,使用者端需要將Token放在請求頭的Authorization欄位中。
jwt是Json Web Token的縮寫,它的結構分為三個部分:header.payload.signature,兩兩之間用【.】分隔。
header是一個JSON結構,主要包含token的型別(即JWT),簽名的演演算法
{ "alg":"HS256", "typ":"JWT" }
payload也是JSON結構,它是存放有效資訊的地方,JWT官方提供了一些官方欄位,你也可以定義自己的私有欄位,其中官方欄位如下:
但是注意,payload是預設不加密的,因此建議自己定義的私有欄位不要放入使用者私密資訊。
它是使用者自己定義的欄位,使用者要設計一個獨一無二且保證不會外洩的金鑰,通過下方演演算法生成簽名,用於未來的身份驗證。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
首先安裝必要的npm包,執行以下指令:
npm i body-parser cors express express-jwt jsonwebtoken,
在index.js中寫入以下內容:
// 匯入 express 模組 const express = require('express') // 建立 express 的伺服器範例 const app = express() // 01:安裝並匯入 JWT 相關的兩個包,分別是 jsonwebtoken 和 express-jwt const jwt = require('jsonwebtoken') const expressJWT = require('express-jwt') // 允許跨域資源共用 const cors = require('cors') app.use(cors()) // 解析 post 表單資料的中介軟體 const bodyParser = require('body-parser') app.use(bodyParser.urlencoded({ extended: false })) // 02:定義 secret 金鑰,建議將金鑰命名為 secretKey const secretKey = 'heyyyyfx' // 04:註冊將 JWT 字串解析還原成 JSON 物件的中介軟體 // 注意:只要設定成功了 express-jwt 這個中介軟體,就可以把解析出來的使用者資訊,掛載到 req.user 屬性上 // unless指定哪些介面不需要存取許可權,即白名單。 app.use(expressJWT({ secret: secretKey }).unless({ path: [/^/api//] })) // 登入介面 app.post('/api/login', function (req, res) { // 將 req.body 請求體中的資料,轉存為 userinfo 常數 const userinfo = req.body // 登入失敗 if (userinfo.username !== 'admin' || userinfo.password !== '000000') { return res.send({ status: 400, message: '登入失敗!', }) } // 登入成功 // 03:在登入成功之後,呼叫 jwt.sign() 方法生成 JWT 字串。並通過 token 屬性傳送給使用者端 // 引數1:使用者的資訊物件 // 引數2:加密的祕鑰 // 引數3:設定物件,可以設定當前 token 的有效期,本處設定的是30S // 記住:千萬不要把密碼加密到 token 字元中 const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' }) res.send({ status: 200, message: '登入成功!', token: tokenStr, // 要傳送給使用者端的 token 字串 }) }) // 這是一個有許可權的 API 介面 app.get('/admin/getinfo', function (req, res) { // 05:使用 req.user 獲取使用者資訊,並使用 data 屬性將使用者資訊傳送給使用者端 console.log(req.user) res.send({ status: 200, message: '獲取使用者資訊成功!', data: req.user, // 要傳送給使用者端的使用者資訊 }) }) // 06:使用全域性錯誤處理中介軟體,捕獲解析 JWT 失敗後產生的錯誤 app.use((err, req, res, next) => { // 這次錯誤是由 token 解析失敗導致的 if (err.name === 'UnauthorizedError') { return res.send({ status: 401, message: '無效的token', }) } res.send({ status: 500, message: '未知的錯誤', }) }) // 呼叫 app.listen 方法,指定埠號並啟動web伺服器 app.listen(8888, function () { console.log('Express server running at http://127.0.0.1:8888') })
開啟node服務後,用postman進行測試,呼叫登入介面後,拿到返回的Token:
隨後呼叫獲取使用者資訊介面,注意,該介面是需要許可權的,我們需要在請求頭中加入Authorization欄位,值為Token,同時有個注意事項,Token值前需要加入Bearer關鍵字,用空格分隔,這是必要的操作。
如果你操作的過慢,就會看到如下報錯,這是因為我們的有效期只有30s。
不妨再呼叫一次登入介面,同時迅速用新的Token請求使用者資訊介面,結果如下,說明成功。
在本文中,我們是自己為Token設定了30s的有效期,但如果你用心觀察國內外的網站,貌似沒有出現用著用著就突然返回到登入介面讓你突然重新登陸的,難道是因為他們的有效期設定的特別長?
其實在真實開發中,Token的有效期往往不會用這種方式設定,大多數有效期是動態的,打個比方,只有當你在當前頁面半小時之內沒有任何請求之後,才會讓你的Token自動失效,這種是怎樣實現的?其實有很多種實現方案,筆者在此只舉一種例子,讀者可以先了解一下redis資料庫。
redis的優點在此不做過多說明,感興趣的可以自行查閱,redis資料庫提供了一個叫expire的命令,命令用於設定 key 的過期時間,key 過期後將不再可用。單位以秒計。
我們可以以此為基礎,當用戶請求登入介面時,我們將Token返回給使用者,同時我們將這個Token作為Key儲存到資料庫,Value為這個使用者的個人資訊或其他內容,併為這個key設定一個定時刪除命令,當用戶在有效期時,資料庫將使用者請求介面時攜帶的Token進行查詢,看是否存在這個Token的key,當可以被查詢時,說明有效期還在(因為過了有效期這個Token就會被刪除,表中就無法查詢到這個Token),同時再次對這個Key執行定時刪除任務,達到覆蓋上一次刪除定時任務,延長有效期的作用,只有當沒有介面請求後,刪除任務執行,Token才會失效,以此來實現動態Token的目的,至於覆蓋定時刪除任務這個操作,因為是每一個操作相關的介面都要進行,因此不妨將它封裝成全域性中介軟體,避免在每個介面中都寫下重複程式碼。
原始碼:https://github.com/fengxiao1998/SessionAndJWT
本文所有內容都是基於node的鑑權,相比於純後端Java開發肯定會有很多不足之處,對於前端而言只是和大家一起了解學習鑑權相關知識,更多關於node Session JWT鑑權登入的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45