前端登陆之cookie篇
一、cookie
上一個文章介紹了實用node寫了一個服務(wù),以及調(diào)用api去操作數(shù)據(jù)庫的一個前后端分離的demo,這次重點記錄一下前后端登陸操作的一個部分。登陸認證是一個項目中比較重要的部分,接下來我將使用cookie的模式來實現(xiàn)登陸認證!
cookie的組成
cookie是瀏覽器中特有的一個概念,它就像瀏覽器的專屬卡包,管理著各個網(wǎng)站的身份信息。
每個cookie就相當于是屬于某個網(wǎng)站的一個卡片,它記錄了下面的信息:
key:鍵,比如「身份編號」
value:值,比如張博的身份編號「14563D1550F2F76D69ECBF4DD54ABC95」,這有點像卡片的條形碼,當然,它可以是任何信息
domain:域,表達這個cookie是屬于哪個網(wǎng)站的,比如123.com,表示這個cookie是屬于123.com這個網(wǎng)站的
path:路徑,表達這個cookie是屬于該網(wǎng)站的哪個基路徑的,就好比是同一家公司不同部門會頒發(fā)不同的出入證。比如/news,表示這個cookie屬于/news這個路徑的。(后續(xù)詳細解釋)
secure:是否使用安全傳輸
expire:過期時間,表示該cookie在什么時候過期
當瀏覽器向服務(wù)器發(fā)送一個請求的時候,它會瞄一眼自己的卡包,看看哪些卡片適合附帶捎給服務(wù)器
如果一個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,則請求協(xié)議必須是https,否則不會發(fā)送該cookie
如果cookie的secure屬性是false,則請求協(xié)議可以是http,也可以是https
如果一個cookie滿足了上述的所有條件,則瀏覽器會把它自動加入到這次請求中
具體加入的方式是,瀏覽器會將符合條件的cookie,自動放置到請求頭中。
如果把它用于登錄場景,就是如下的流程:
登錄請求
瀏覽器發(fā)送請求到服務(wù)器,附帶賬號密碼
服務(wù)器驗證賬號密碼是否正確,如果不正確,響應(yīng)錯誤,如果正確,在響應(yīng)頭中設(shè)置cookie,附帶登錄認證信息(至于登錄認證信息是設(shè)么樣的,如何設(shè)計,要考慮哪些問題,就是另一個話題了,可以百度 jwt)
客戶端收到cookie,瀏覽器自動記錄下來
后續(xù)請求
瀏覽器發(fā)送請求到服務(wù)器,希望添加一個管理員,并將cookie自動附帶到請求中
服務(wù)器先獲取cookie,驗證cookie中的信息是否正確,如果不正確,不予以操作,如果正確,完成正常的業(yè)務(wù)流程
二、處理登陸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存在則登錄成功//設(shè)置cookie//給瀏覽器用cookie//cookie-parse給了一個默認加密,其中有默認密鑰可以設(shè)置const value = result.idcryptor.encrypt(value)res.cookie("token",result.id,{path:"/",domain:"localhost",maxAge:3600*24*10000,// signed:true,})//給其他應(yīng)用 如手機端res.header("authorization",value)}return result;} ))當我們調(diào)用這個接口之后,就會從數(shù)據(jù)庫中訪問這個用戶(根據(jù)用戶名和密碼),這個密碼時使用md5進行加密,然后解密的:
當返回這個結(jié)果的時候,我們就使用cooke方法進行給res請求結(jié)果中設(shè)置cookies,將token設(shè)為用戶的id,剩下的domain,還有path,maxAge都可以視情況而定,但是這里的id就可以在瀏覽器端使用document.cookie獲取到,然后提取出來id,我們在服務(wù)端,也可以設(shè)置讓js不能獲取cookie,防止跨站腳本攻擊,但是還是不安全,因為這個數(shù)據(jù)是明文的,可以在瀏覽器中的cookie中看到數(shù)據(jù),而cookie-parse里面提供一個自動加密的屬性,就是signed為true,就會給這個cookie里的token值自動加密,之后取到也可以自動解密,但是有個很嚴重的問題,這里cookie是瀏覽器特有的一個屬性,在其他端雖然可以使用但是沒有像瀏覽器這樣專門設(shè)置這個cookie模塊,來處理請求,所以我們在這個請求頭中,留一手,給請求頭中再設(shè)置一個authorization的值為用戶id,這樣不管是pad端,手機端都可以處理這個請求內(nèi)容,所以當設(shè)置這個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文件導出兩個函數(shù),一個用于加密,一個用語解密;
使用對稱加密算法:aes算法 128位密鑰(16個字節(jié)的字符串),其中mm7h3ck87ugk9l4a是用隨機數(shù)轉(zhuǎn)字符串拼接獲取Math.random(),toString(36).slice(-8)+Math.random(),toString(36).slice(-8)
Buffer是node里面的一個類,JS里的String對象,存儲的是字符串,而且是Unicode編碼的。
我們使用node內(nèi)置庫crypto,crypto.getCiphers()可以看到crypto所有的加密函數(shù),而我們使用第一個aes-128-cbc,接下來是準備一個iv,隨機向量 ,一般來說密鑰固定,向量是不固定的,這里我為了簡便實用固定的。而這個密鑰是存儲在服務(wù)端的,所以很難進行解密,這樣就會讓數(shù)據(jù)有安全保障。第一層保障是這個密鑰,如果這個密鑰泄漏,下一個保障就是iv向量,這里寫的固定的,當然也可以寫那種不固定的,使用到j(luò)s中的閉包;
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;},} }這里我們直接導出一個函數(shù),這個函數(shù)執(zhí)行后就會給予一個對象,對象有兩個方法,第一個是加密,第二個是解密,而這兩個函數(shù)都使用到函數(shù)里定義的變量iv,當我們調(diào)用一次這個函數(shù),就會生成一個iv,所以這個iv是不固定的,也是無法獲取的,這樣就完善了token加密的一個步驟,讓數(shù)據(jù)更加的安全!
三、使用中間件獲取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)); }以上可以看出,當我們調(diào)用接口的時候,就會進行token認證,首先從req中獲取cookie如果沒有的話,就會在authorization中獲取,如果都沒有那么就報錯,如果有一個有,就進行解密,然后執(zhí)行下一個中間件,至此,使用cookie方式登陸的模式完成!
總結(jié)
以上是生活随笔為你收集整理的前端登陆之cookie篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python学习之——综合小游戏
- 下一篇: 微信小程序纯前端生成海报并保存本地