前端登陆之cookie篇
一、cookie
上一個文章介紹了實用node寫了一個服務,以及調用api去操作數據庫的一個前后端分離的demo,這次重點記錄一下前后端登陸操作的一個部分。登陸認證是一個項目中比較重要的部分,接下來我將使用cookie的模式來實現登陸認證!
cookie的組成
cookie是瀏覽器中特有的一個概念,它就像瀏覽器的專屬卡包,管理著各個網站的身份信息。
每個cookie就相當于是屬于某個網站的一個卡片,它記錄了下面的信息:
key:鍵,比如「身份編號」
value:值,比如張博的身份編號「14563D1550F2F76D69ECBF4DD54ABC95」,這有點像卡片的條形碼,當然,它可以是任何信息
domain:域,表達這個cookie是屬于哪個網站的,比如123.com,表示這個cookie是屬于123.com這個網站的
path:路徑,表達這個cookie是屬于該網站的哪個基路徑的,就好比是同一家公司不同部門會頒發不同的出入證。比如/news,表示這個cookie屬于/news這個路徑的。(后續詳細解釋)
secure:是否使用安全傳輸
expire:過期時間,表示該cookie在什么時候過期
當瀏覽器向服務器發送一個請求的時候,它會瞄一眼自己的卡包,看看哪些卡片適合附帶捎給服務器
如果一個cookie同時滿足以下條件,則這個cookie會被附帶到請求中
cookie沒有過期
cookie中的域和這次請求的域是匹配的
比如cookie中的域是123.com,則可以匹配的請求域是123.com、www.123.com、234.123.com等等
比如cookie中的域是www.123.com,則只能匹配www.123.com這樣的請求域
cookie是不在乎端口的,只要域匹配即可
cookie中的path和這次請求的path是匹配的
比如cookie中的path是/news,則可以匹配的請求路徑可以是/news、/news/detail、/news/a/b/c等等,但不能匹配/blogs
如果cookie的path是/,可以想象,能夠匹配所有的路徑
驗證cookie的安全傳輸
如果cookie的secure屬性是true,則請求協議必須是https,否則不會發送該cookie
如果cookie的secure屬性是false,則請求協議可以是http,也可以是https
如果一個cookie滿足了上述的所有條件,則瀏覽器會把它自動加入到這次請求中
具體加入的方式是,瀏覽器會將符合條件的cookie,自動放置到請求頭中。
如果把它用于登錄場景,就是如下的流程:
登錄請求
瀏覽器發送請求到服務器,附帶賬號密碼
服務器驗證賬號密碼是否正確,如果不正確,響應錯誤,如果正確,在響應頭中設置cookie,附帶登錄認證信息(至于登錄認證信息是設么樣的,如何設計,要考慮哪些問題,就是另一個話題了,可以百度 jwt)
客戶端收到cookie,瀏覽器自動記錄下來
后續請求
瀏覽器發送請求到服務器,希望添加一個管理員,并將cookie自動附帶到請求中
服務器先獲取cookie,驗證cookie中的信息是否正確,如果不正確,不予以操作,如果正確,完成正常的業務流程
二、處理登陸APi接口
接下來就是處理登陸這個api接口:
const express = require("express") const router = express.Router() const user = require("../../serveAPI/userService") const {asyncHandler} = require("../getResult") const cryptor = require("../../util/crypt")router.post("/login",asyncHandler(async (req,res,next) =>{const result = await user.login(req.body.loginId,req.body.loginPwd)if (result) {//如果result存在則登錄成功//設置cookie//給瀏覽器用cookie//cookie-parse給了一個默認加密,其中有默認密鑰可以設置const value = result.idcryptor.encrypt(value)res.cookie("token",result.id,{path:"/",domain:"localhost",maxAge:3600*24*10000,// signed:true,})//給其他應用 如手機端res.header("authorization",value)}return result;} ))當我們調用這個接口之后,就會從數據庫中訪問這個用戶(根據用戶名和密碼),這個密碼時使用md5進行加密,然后解密的:
當返回這個結果的時候,我們就使用cooke方法進行給res請求結果中設置cookies,將token設為用戶的id,剩下的domain,還有path,maxAge都可以視情況而定,但是這里的id就可以在瀏覽器端使用document.cookie獲取到,然后提取出來id,我們在服務端,也可以設置讓js不能獲取cookie,防止跨站腳本攻擊,但是還是不安全,因為這個數據是明文的,可以在瀏覽器中的cookie中看到數據,而cookie-parse里面提供一個自動加密的屬性,就是signed為true,就會給這個cookie里的token值自動加密,之后取到也可以自動解密,但是有個很嚴重的問題,這里cookie是瀏覽器特有的一個屬性,在其他端雖然可以使用但是沒有像瀏覽器這樣專門設置這個cookie模塊,來處理請求,所以我們在這個請求頭中,留一手,給請求頭中再設置一個authorization的值為用戶id,這樣不管是pad端,手機端都可以處理這個請求內容,所以當設置這個authorization這個鍵之后,cookie-parse只能去處理這個cookie中的token而不能處理authorization,所以加密這個事情還得我們來做,這里使用node里面提供的這個crypto模塊,來進行加密:
const secret = Buffer.from("mm7h3ck87ugk9l4a"); const crypto = require("crypto"); const iv = Buffer.from("jxkvxz97409u3m8c");//加密一個字符串 exports.encrypt = function (str) {const cry = crypto.createCipheriv("aes-128-cbc", secret, iv);let result = cry.update(str, "utf-8", "hex");result += cry.final("hex");return result; };//解密一個字符串 exports.decrypt = function (str) {const decry = crypto.createDecipheriv("aes-128-cbc", secret, iv);let result = decry.update(str, "hex", "utf-8");result += decry.final("utf-8");return result; };上面是一個js文件導出兩個函數,一個用于加密,一個用語解密;
使用對稱加密算法:aes算法 128位密鑰(16個字節的字符串),其中mm7h3ck87ugk9l4a是用隨機數轉字符串拼接獲取Math.random(),toString(36).slice(-8)+Math.random(),toString(36).slice(-8)
Buffer是node里面的一個類,JS里的String對象,存儲的是字符串,而且是Unicode編碼的。
我們使用node內置庫crypto,crypto.getCiphers()可以看到crypto所有的加密函數,而我們使用第一個aes-128-cbc,接下來是準備一個iv,隨機向量 ,一般來說密鑰固定,向量是不固定的,這里我為了簡便實用固定的。而這個密鑰是存儲在服務端的,所以很難進行解密,這樣就會讓數據有安全保障。第一層保障是這個密鑰,如果這個密鑰泄漏,下一個保障就是iv向量,這里寫的固定的,當然也可以寫那種不固定的,使用到js中的閉包;
module.exports = function(){//準備一個ivconst iv = Buffer.from(Math.random(),toString(36).slice(-8)+Math.random(),toString(36).slice(-8));return {encrypt(str){const cry = crypto.createCipheriv("aes-128-cbc", secret, iv);let result = cry.update(str, "utf-8", "hex");result += cry.final("hex");return result;},decrypt(str){const decry = crypto.createDecipheriv("aes-128-cbc", secret, iv);let result = decry.update(str, "hex", "utf-8");result += decry.final("utf-8");return result;},} }這里我們直接導出一個函數,這個函數執行后就會給予一個對象,對象有兩個方法,第一個是加密,第二個是解密,而這兩個函數都使用到函數里定義的變量iv,當我們調用一次這個函數,就會生成一個iv,所以這個iv是不固定的,也是無法獲取的,這樣就完善了token加密的一個步驟,讓數據更加的安全!
三、使用中間件獲取cookie
我們使用cookie-parser插件寫入中間件,之后再每一次的請求中都會在req對象中注入cookies屬性,用于獲取傳遞過來的cookie,也會在res對象中注入獲取cookie的方法,所以操作起來就更加方便。
我們引入自己寫的cookieMiddleware中間件
/*** 用于解析token*/ const { getErr } = require("./getResult"); const cryptor = require("../util/crypt")// 用于解析token module.exports = (req, res, next) => {let token = req.cookies.token;// 獲取cokkie // let token = req.signedCookies.token;if(!token){//其他的端獲取tokentoken = req.headers.authorization}if(!token){handleNonToken(req,res,next)return;}const userId = cryptor.decrypt(token)console.log(userId,"認證通過啦~~~");next() };//處理沒有認證的情況 function handleNonToken(req, res, next) {res.status(403).send(getErr("不好意思,您沒有token令牌無法登陸哦~", 403)); }以上可以看出,當我們調用接口的時候,就會進行token認證,首先從req中獲取cookie如果沒有的話,就會在authorization中獲取,如果都沒有那么就報錯,如果有一個有,就進行解密,然后執行下一個中間件,至此,使用cookie方式登陸的模式完成!
總結
以上是生活随笔為你收集整理的前端登陆之cookie篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python学习之——综合小游戏
- 下一篇: 2017年html5行业报告,云适配发布