<address id="ttjl9"></address>

      <noframes id="ttjl9"><address id="ttjl9"><nobr id="ttjl9"></nobr></address>
      <form id="ttjl9"></form>
        <em id="ttjl9"><span id="ttjl9"></span></em>
        <address id="ttjl9"></address>

          <noframes id="ttjl9"><form id="ttjl9"></form>

          前后端的身份認證

          2023-1-16    前端達人

          1、Web 開發模式

          目前主流的 Web 開發模式有兩種,分別是:

          • 基于服務端渲染的傳統 Web 開發模式
          • 基于前后端分離的新型 Web 開發模式

          1.1、服務端渲染的 Web 開發模式

                  服務端渲染的概念:服務器發送給客戶端的 HTML 頁面,是在服務器通過字符串的拼接,動態生成的。因此,客戶端不需要使用 Ajax 這樣的技術額外請求頁面的數據。代碼示例如下:

          1.2、服務端渲染的優缺點 

          優點:

          •  前端耗時少。因為服務器端負責動態生成 HTML 內容,瀏覽器只需要直接渲染頁面即可。尤其是移動端,更省電。
          • 有利于SEO。因為服務器端響應的是完整的 HTML 頁面內容,所以爬蟲更容易爬取獲得信息,更有利于 SEO

          缺點:

          • 占用服務器端資源。即服務器端完成 HTML 頁面內容的拼接,如果請求較多,會對服務器造成一定的訪問壓力。
          • 不利于前后端分離,開發效率低。使用服務器端渲染,則無法進行分工合作,尤其對于前端復雜度高的項目,不利于項目高效開發。

          1.3、前后端分離的 Web 開發模式

                  前后端分離的概念:前后端分離的開發模式,依賴于 Ajax 技術的廣泛應用。簡而言之,前后端分離的 Web 開發模式,就是后端只負責提供 API 接口,前端使用 Ajax 調用接口的開發模式。

          1.4、前后端分離的優缺點

          優點:

          • 開發體驗好。前端專注于 UI 頁面的開發,后端專注于api 的開發,且前端有更多的選擇性。
          • 用戶體驗好。Ajax 技術的廣泛應用,極大的提高了用戶的體驗,可以輕松實現頁面的局部刷新。
          • 減輕了服務器端的渲染壓力。因為頁面最終是在每個用戶的瀏覽器中生成的。

          缺點:

                  不利于 SEO。因為完整的 HTML 頁面需要在客戶端動態拼接完成,所以爬蟲對無法爬取頁面的有效信息。(解決方案:利用 VueReact 等前端框架的 SSR server side render)技術能夠很好的解決 SEO 問題?。?/span>

          1.5、如何選擇 Web 開發模式

          不談業務場景而盲目選擇使用何種開發模式都是耍流氓。

          • 比如企業級網站,主要功能是展示而沒有復雜的交互,并且需要良好的 SEO,則這時我們就需要使用服務器端渲染;
          • 而類似后臺管理項目,交互性比較強,不需要考慮 SEO,那么就可以使用前后端分離的開發模式。

                  另外,具體使用何種開發模式并不是絕對的,為了同時兼顧首頁的渲染速度前后端分離的開發效率,一些網站采用了首屏服務器端渲染 其他頁面前后端分離的開發模式。

          2、身份認證

          2.1、什么是身份認證

                  身份認證Authentication)又稱“身份驗證”、“鑒權”,是指通過一定的手段,完成對用戶身份的確認。

          • 日常生活中的身份認證隨處可見,例如:高鐵的驗票乘車,手機的密碼或指紋解鎖,支付寶或微信的支付密碼等。
          • 在 Web 開發中,也涉及到用戶身份的認證,例如:各大網站的手機驗證碼登錄、郵箱密碼登錄、二維碼登錄等。

          2.2、為什么需要身份認證

                  身份認證的目的,是為了確認當前所聲稱為某種身份的用戶,確實是所聲稱的用戶。例如,你去找快遞員取快遞,你要怎么證明這份快遞是你的。

                  在互聯網項目開發中,如何對用戶的身份進行認證,是一個值得深入探討的問題。例如,如何才能保證網站不會錯誤的將“馬云的存款數額”顯示到“馬化騰的賬戶”上。

          2.3、不同開發模式下的身份認證

          對于服務端渲染前后端分離這兩種開發模式來說,分別有著不同的身份認證方案:

          • 服務端渲染推薦使用 Session 認證機制
          • 前后端分離推薦使用 JWT 認證機制

          3、Session 認證機制

          3.1、HTTP 協議的無狀態性

          • 了解 HTTP 協議的無狀態性是進一步學習 Session 認證機制的必要前提。
          • HTTP 協議的無狀態性,指的是客戶端的每次 HTTP 請求都是獨立的,連續多個請求之間沒有直接的關系,服務器不會主動保留每次 HTTP 請求的狀態。

          3.2、如何突破 HTTP 無狀態的限制 

                  對于超市來說,為了方便收銀員在進行結算時給 VIP 用戶打折,超市可以為每個 VIP 用戶發放會員卡。

          注意:現實生活中的會員卡身份認證方式,在 Web 開發中的專業術語叫做 Cookie 

          3.3、什么是 Cookie

          • Cookie 存儲在用戶瀏覽器中的一段不超過 4 KB 的字符串。它由一個名稱Name)、一個Value)和其它幾個用于控制 Cookie 有效期、安全性、使用范圍可選屬性組成。
          • 不同域名下的 Cookie 各自獨立,每當客戶端發起請求時,會自動當前域名下所有未過期的 Cookie 一同發送到服務器。

          Cookie的幾大特性:

          1. 自動發送
          2. 域名獨立
          3. 過期時限
          4. 4KB 限制

          3.4、Cookie 在身份認證中的作用

                  客戶端第一次請求服務器的時候,服務器通過響應頭的形式,向客戶端發送一個身份認證的 Cookie,客戶端會自動將 Cookie 保存在瀏覽器中。

                  隨后,當客戶端瀏覽器每次請求服務器的時候,瀏覽器會自動將身份認證相關的 Cookie,通過請求頭的形式發送給服務器,服務器即可驗明客戶端的身份。

          3.5、Cookie 不具有安全性 

                  由于 Cookie 是存儲在瀏覽器中的,而且瀏覽器也提供了讀寫 Cookie 的 API,因此 Cookie 很容易被偽造,不具有安全性。因此不建議服務器將重要的隱私數據,通過 Cookie 的形式發送給瀏覽器。

          注意:千萬不要使用 Cookie 存儲重要且隱私的數據!比如用戶的身份信息、密碼等。 

          3.6、提高身份認證的安全性

                  為了防止客戶偽造會員卡,收銀員在拿到客戶出示的會員卡之后,可以在收銀機上進行刷卡認證。只有收銀機確認存在的會員卡,才能被正常使用。

          這種“會員卡 + 刷卡認證”的設計理念,就是 Session 認證機制的精髓。 

          3.7、Session 工作原理

           

          4、在 Express 中使用 Session 認證

          4.1、安裝 express-session 中間件

          在 Express 項目中,只需要安裝 express-session 中間件,即可在項目中使用 Session 認證:

          npm install express-session

          4.2、配置 express-session 中間件

                  express-session 中間件安裝成功后,需要通過 app.use() 注冊 session 中間件,示例代碼如下:

           
          
          1. // 導入 session 中間件
          2. const session = require('express-session')
          3. // 配置 session 中間件
          4. app.use(
          5. session({
          6. secret: 'itheima', // secret 屬性的值可以為任意字符串
          7. resave: false, // 固定寫法
          8. saveUninitialized: true, // 固定寫法
          9. })
          10. )

          4.3、向 session 存數據

                  當 express-session 中間件配置成功后,即可通過 req.session 來訪問和使用 session 對象,從而存儲用戶的關鍵信息:

           
          
          1. // 登錄的 API 接口
          2. app.post('/api/login', (req, res) => {
          3. // 判斷用戶提交的登錄信息是否正確
          4. if (req.body.username !== 'admin' || req.body.password !== '000000') {
          5. return res.send({ status: 1, msg: '登錄失敗' })
          6. }
          7. // TODO_02:請將登錄成功后的用戶信息,保存到 Session 中
          8. // 注意:只有成功配置了 express-session 這個中間件之后,才能夠通過 req 點出來 session 這個屬性
          9. req.session.user = req.body // 用戶的信息
          10. console.log(req.body)
          11. req.session.islogin = true // 用戶的登錄狀態
          12. res.send({ status: 0, msg: '登錄成功' })
          13. })

          4.4、從 session 取數據

          可以直接從 req.session 對象上獲取之前存儲的數據,示例代碼如下:

           
          
          1. // 獲取用戶姓名的接口
          2. app.get('/api/username', (req, res) => {
          3. // TODO_03:請從 Session 中獲取用戶的名稱,響應給客戶端
          4. if (!req.session.islogin) {
          5. return res.send({ status: 1, msg: 'fail' })
          6. }
          7. res.send({
          8. status: 0,
          9. msg: 'success',
          10. username: req.session.user.username,
          11. })
          12. })

          4.5、清空 session

          調用 req.session.destroy() 函數,即可清空服務器保存的 session 信息。

           
          
          1. // 退出登錄的接口
          2. app.post('/api/logout', (req, res) => {
          3. // TODO_04:清空 Session 信息
          4. req.session.destroy()
          5. res.send({
          6. status: 0,
          7. msg: '退出登錄成功',
          8. })
          9. })

          4.6、完整示例

          index.html 

           
          
          1. <!DOCTYPE html>
          2. <html lang="en">
          3. <head>
          4. <meta charset="UTF-8">
          5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
          6. <title>后臺主頁</title>
          7. <script src="./jquery.js"></script>
          8. </head>
          9. <body>
          10. <h1>首頁</h1>
          11. <button id="btnLogout">退出登錄</button>
          12. <script>
          13. $(function () {
          14. // 頁面加載完成后,自動發起請求,獲取用戶姓名
          15. $.get('/api/username', function (res) {
          16. // status 為 0 表示獲取用戶名稱成功;否則表示獲取用戶名稱失敗!
          17. if (res.status !== 0) {
          18. alert('您尚未登錄,請登錄后再執行此操作!')
          19. location.href = './login.html'
          20. } else {
          21. alert('歡迎您:' + res.username)
          22. }
          23. })
          24. // 點擊按鈕退出登錄
          25. $('#btnLogout').on('click', function () {
          26. // 發起 POST 請求,退出登錄
          27. $.post('/api/logout', function (res) {
          28. if (res.status === 0) {
          29. // 如果 status 為 0,則表示退出成功,重新跳轉到登錄頁面
          30. location.href = './login.html'
          31. }
          32. })
          33. })
          34. })
          35. </script>
          36. </body>
          37. </html>

          login.html

           
          
          1. <!DOCTYPE html>
          2. <html lang="en">
          3. <head>
          4. <meta charset="UTF-8">
          5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
          6. <title>登錄頁面</title>
          7. <script src="./jquery.js"></script>
          8. </head>
          9. <body>
          10. <!-- 登錄表單 -->
          11. <form id="form1">
          12. <div>賬號:<input type="text" name="username" autocomplete="off" /></div>
          13. <div>密碼:<input type="password" name="password" /></div>
          14. <button>登錄</button>
          15. </form>
          16. <script>
          17. $(function () {
          18. // 監聽表單的提交事件
          19. $('#form1').on('submit', function (e) {
          20. // 阻止默認提交行為
          21. e.preventDefault()
          22. // 發起 POST 登錄請求
          23. $.post('/api/login', $(this).serialize(), function (res) {
          24. // status 為 0 表示登錄成功;否則表示登錄失?。?/span>
          25. if (res.status === 0) {
          26. location.href = './index.html'
          27. } else {
          28. alert('登錄失??!')
          29. }
          30. })
          31. })
          32. })
          33. </script>
          34. </body>
          35. </html>

          app.js

           
          
          1. // 導入 express 模塊
          2. const express = require('express')
          3. // 創建 express 的服務器實例
          4. const app = express()
          5. // TODO_01:請配置 Session 中間件
          6. const session = require('express-session')
          7. app.use(
          8. session({
          9. secret: 'itheima',
          10. resave: false,
          11. saveUninitialized: true,
          12. })
          13. )
          14. // 托管靜態頁面
          15. app.use(express.static('./pages'))
          16. // 解析 POST 提交過來的表單數據
          17. app.use(express.urlencoded({ extended: false }))
          18. // 登錄的 API 接口
          19. app.post('/api/login', (req, res) => {
          20. // 判斷用戶提交的登錄信息是否正確
          21. if (req.body.username !== 'admin' || req.body.password !== '000000') {
          22. return res.send({ status: 1, msg: '登錄失敗' })
          23. }
          24. // TODO_02:請將登錄成功后的用戶信息,保存到 Session 中
          25. // 注意:只有成功配置了 express-session 這個中間件之后,才能夠通過 req 點出來 session 這個屬性
          26. req.session.user = req.body // 用戶的信息
          27. console.log(req.body)
          28. req.session.islogin = true // 用戶的登錄狀態
          29. res.send({ status: 0, msg: '登錄成功' })
          30. })
          31. // 獲取用戶姓名的接口
          32. app.get('/api/username', (req, res) => {
          33. // TODO_03:請從 Session 中獲取用戶的名稱,響應給客戶端
          34. if (!req.session.islogin) {
          35. return res.send({ status: 1, msg: 'fail' })
          36. }
          37. res.send({
          38. status: 0,
          39. msg: 'success',
          40. username: req.session.user.username,
          41. })
          42. })
          43. // 退出登錄的接口
          44. app.post('/api/logout', (req, res) => {
          45. // TODO_04:清空 Session 信息
          46. req.session.destroy()
          47. res.send({
          48. status: 0,
          49. msg: '退出登錄成功',
          50. })
          51. })
          52. // 調用 app.listen 方法,指定端口號并啟動web服務器
          53. app.listen(80, function () {
          54. console.log('Express server running at http://127.0.0.1:80')
          55. })

          5、JWT 認證機制

          5.1、了解 Session 認證的局限性

                  Session 認證機制需要配合 Cookie 才能實現。由于 Cookie 默認不支持跨域訪問,所以,當涉及到前端跨域請求后端接口的時候,需要做很多額外的配置,才能實現跨域 Session 認證。

          注意:

          • 當前端請求后端接口不存在跨域問題的時候,推薦使用 Session 身份認證機制。
          • 當前端需要跨域請求后端接口的時候,不推薦使用 Session 身份認證機制,推薦使用 JWT 認證機制。

          5.2、什么是 JWT

          JWT(英文全稱:JSON Web Token)是目前最流行跨域認證解決方案。

          5.3、JWT 工作原理

          總結:用戶的信息通過 Token 字符串的形式,保存在客戶端瀏覽器中。服務器通過還原 Token 字符串的形式來認證用戶的身份。 

          5.4、JWT 組成部分

          JWT 通常由三部分組成,分別是 Header(頭部)、Payload(有效荷載)、Signature(簽名)。

          三者之間使用英文的“.”分隔,格式如下:

          下面是 JWT 字符串的示例: 

          5.5、JWT 三個部分各自代表的含義 

          JWT 的三個組成部分,從前到后分別是 Header、PayloadSignature。

          其中:

          • Payload 部分才是真正的用戶信息,它是用戶信息經過加密之后生成的字符串。
          • Header 和 Signature 安全性相關的部分,只是為了保證 Token 的安全性。

          5.6、JWT 使用方式 

          客戶端收到服務器返回的 JWT 之后,通常會將它儲存在 localStorage 或 sessionStorage 中。

          此后,客戶端每次與服務器通信,都要帶上這個 JWT 的字符串,從而進行身份認證。推薦的做法是把 JWT 放在 HTTP 請求頭的 Authorization 字段中,格式如下:

           

          6、在 Express 中使用 JWT

          6.1、安裝 JWT 相關的包

          運行如下命令,安裝如下兩個 JWT 相關的包:

          npm install jsonwebtoken express-jwt

          其中:

          • jsonwebtoken 用于生成 JWT 字符串
          • express-jwt 用于將 JWT 字符串解析還原成 JSON 對象

          6.2、導入 JWT 相關的包

          使用 require() 函數,分別導入 JWT 相關的兩個包:

           
          
          1. // 安裝并導入 JWT 相關的兩個包,分別是 jsonwebtoken 和 express-jwt
          2. const jwt = require('jsonwebtoken')
          3. const expressJWT = require('express-jwt')

          6.3、定義 secret 密鑰

                  為了保證 JWT 字符串的安全性,防止 JWT 字符串在網絡傳輸過程中被別人破解,我們需要專門定義一個用于加密解密的 secret 密鑰:

          • 當生成 JWT 字符串的時候,需要使用 secret 密鑰對用戶的信息進行加密,最終得到加密好的 JWT 字符串
          • 當把 JWT 字符串解析還原成 JSON 對象的時候,需要使用 secret 密鑰進行解密
           
          
          1. // 定義 secret 密鑰,建議將密鑰命名為 secretKey,本質上就是一個字符串
          2. const secretKey = 'itheima No1 ^_^'

          6.4、在登錄成功后生成 JWT 字符串

          調用 jsonwebtoken 包提供的 sign() 方法,將用戶的信息加密成 JWT 字符串,響應給客戶端:

           
          
          1. // 登錄接口
          2. app.post('/api/login', function (req, res) {
          3. // 將 req.body 請求體中的數據,轉存為 userinfo 常量
          4. const userinfo = req.body
          5. // 登錄失敗
          6. if (userinfo.username !== 'admin' || userinfo.password !== '000000') {
          7. return res.send({
          8. status: 400,
          9. message: '登錄失敗!',
          10. })
          11. }
          12. // 登錄成功
          13. // TODO_03:在登錄成功之后,調用 jwt.sign() 方法生成 JWT 字符串。并通過 token 屬性發送給客戶端
          14. // 參數1:用戶的信息對象
          15. // 參數2:加密的秘鑰
          16. // 參數3:配置對象,可以配置當前 token 的有效期
          17. // 記?。呵f不要把密碼加密到 token 字符中
          18. const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' })
          19. res.send({
          20. status: 200,
          21. message: '登錄成功!',
          22. token: tokenStr, // 要發送給客戶端的 token 字符串
          23. })
          24. })

          6.5、將 JWT 字符串還原為 JSON 對象

          • 客戶端每次在訪問那些有權限接口的時候,都需要主動通過請求頭中的 Authorization 字段,將 Token 字符串發送到服務器進行身份認證。
          • 此時,服務器可以通過 express-jwt 這個中間件,自動將客戶端發送過來的 Token 解析還原成 JSON 對象:
           
          
          1. // 使用 app.use() 來注冊中間件
          2. // expressJWT({ secret: secretKey }) 就是用來解析 Token 的中間件
          3. // .unless({ path: [/^\/api\//] }) 用來指定哪些接口不需要訪問權限
          4. app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }))

          6.6、使用 req.user 獲取用戶信息

          當 express-jwt 這個中間件配置成功之后,即可在那些有權限的接口中,使用 req.user 對象,來訪問從 JWT 字符串中解析出來的用戶信息了,示例代碼如下: 

           
          
          1. // 這是一個有權限的 API 接口
          2. app.get('/admin/getinfo', function (req, res) {
          3. // TODO_05:使用 req.user 獲取用戶信息,并使用 data 屬性將用戶信息發送給客戶端
          4. console.log(req.user)
          5. res.send({
          6. status: 200,
          7. message: '獲取用戶信息成功!',
          8. data: req.user, // 要發送給客戶端的用戶信息
          9. })
          10. })

          6.7、捕獲解析 JWT 失敗后產生的錯誤 

          藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請加藍小助,微信號:ben_lanlan,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系01063334945。


          分享此文一切功德,皆悉回向給文章原作者及眾讀者.
          免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。


          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務、UI設計公司、界面設計公司、UI設計服務公司、數據可視化設計公司、UI交互設計公司、高端網站設計公司、UI咨詢、用戶體驗公司、軟件界面設計公司


          日歷

          鏈接

          個人資料

          藍藍設計的小編 http://www.syprn.cn

          存檔

          亚洲va欧美va天堂v国产综合