close
很多人在用localStorage或sessionStorage的時候喜歡直接用,明文存儲,直接將信息暴露在;瀏覽器中,雖然一般場景下都能應付得了且簡單粗暴,但特殊需求情況下,比如設置定時功能,就不能實現。就需要對其進行二次封裝,為了在使用上增加些安全感,那加密也必然是少不了的了。為方便項目使用,特對常規操作進行封裝。不完善之處會進一步更新...(更新於:2022.06.02 16:30)

設計

封裝之前先梳理下所需功能,並要做成什麼樣,採用什麼樣的規範,部分主要代碼片段是以localStorage作為示例,最後會貼出完整代碼的。可以結合項目自行優化,也可以直接使用。

//區分存儲類型type//自定義名稱前綴prefix//支持設置過期時間expire//支持加密可選,開發環境下未方便調試可關閉加密//支持數據加密這裡採用crypto-js加密也可使用其他方式//判斷是否支持StorageisSupportStorage//設置setStorage//獲取getStorage//是否存在hasStorage//獲取所有keygetStorageKeys//根據索引獲取keygetStorageForIndex//獲取localStorage長度getStorageLength//獲取全部getAllStorage//刪除removeStorage//清空clearStorage//定義參數類型window.localStorage,window.sessionStorage,constconfig={type:'localStorage',//本地存儲類型localStorage/sessionStorageprefix:'SDF_0.0.1',//名稱前綴建議:項目名+項目版本expire:1,//過期時間單位:秒isEncrypt:true//默認加密為了調試方便,開發過程中可以不加密}複製代碼設置 setStorage

Storage 本身是不支持過期時間設置的,要支持設置過期時間,可以效仿 Cookie 的做法,setStorage(key,value,expire)方法,接收三個參數,第三個參數就是設置過期時間的,用相對時間,單位秒,要對所傳參數進行類型檢查。可以設置統一的過期時間,也可以對單個值得過期時間進行單獨配置。兩種方式按需配置。

代碼實現:

//設置setStorageexportconstsetStorage=(key,value,expire=0)=>{if(value===''||value===null||value===undefined){value=null;}if(isNaN(expire)||expire<1)thrownewError("Expiremustbeanumber");expire=(expire?expire:config.expire)*60000;letdata={value:value,//存儲值time:Date.now(),//存值時間戳expire:expire//過期時間}window[config.type].setItem(key,JSON.stringify(data));}複製代碼獲取 getStorage

首先要對key是否存在進行判斷,防止獲取不存在的值而報錯。對獲取方法進一步擴展,只要在有效期內獲取Storage值,就對過期時間進行續期,如果過期則直接刪除該值。並返回null

//獲取getStorageexportconstgetStorage=(key)=>{//key不存在判斷if(!window[config.type].getItem(key)||JSON.stringify(window[config.type].getItem(key))==='null'){returnnull;}//優化持續使用中續期conststorage=JSON.parse(window[config.type].getItem(key));console.log(storage)letnowTime=Date.now();console.log(config.expire*6000,(nowTime-storage.time))//過期刪除if(storage.expire&&config.expire*6000<(nowTime-storage.time)){removeStorage(key);returnnull;}else{//未過期期間被調用則自動續期進行保活setStorage(key,storage.value);returnstorage.value;}}複製代碼獲取所有值//獲取全部getAllStorageexportconstgetAllStorage=()=>{letlen=window[config.type].length//獲取長度letarr=newArray()//定義數據集for(leti=0;i<len;i++){//獲取key索引從0開始letgetKey=window[config.type].key(i)//獲取key對應的值letgetVal=window[config.type].getItem(getKey)//放進數組arr[i]={'key':getKey,'val':getVal,}}returnarr}複製代碼刪除 removeStorage//名稱前自動添加前綴constautoAddPrefix=(key)=>{constprefix=config.prefix?config.prefix+'_':'';returnprefix+key;}//刪除removeStorageexportconstremoveStorage=(key)=>{window[config.type].removeItem(autoAddPrefix(key));}複製代碼清空 clearStorage//清空clearStorageexportconstclearStorage=()=>{window[config.type].clear();}複製代碼加密、解密

加密採用的是crypto-js

//安裝crypto-jsnpminstallcrypto-js//引入crypto-js有以下兩種方式importCryptoJSfrom"crypto-js";//或者constCryptoJS=require("crypto-js");複製代碼

對crypto-js設置密鑰和密鑰偏移量,可以採用將一個私鑰經MD5加密生成16位密鑰獲得。

//十六位十六進制數作為密鑰constSECRET_KEY=CryptoJS.enc.Utf8.parse("3333e6e143439161");//十六位十六進制數作為密鑰偏移量constSECRET_IV=CryptoJS.enc.Utf8.parse("e3bbe7e3ba84431a");複製代碼

對加密方法進行封裝

/***加密方法*@paramdata*@returns{string}*/exportfunctionencrypt(data){if(typeofdata==="object"){try{data=JSON.stringify(data);}catch(error){console.log("encrypterror:",error);}}constdataHex=CryptoJS.enc.Utf8.parse(data);constencrypted=CryptoJS.AES.encrypt(dataHex,SECRET_KEY,{iv:SECRET_IV,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});returnencrypted.ciphertext.toString();}複製代碼

對解密方法進行封裝

/***解密方法*@paramdata*@returns{string}*/exportfunctiondecrypt(data){constencryptedHexStr=CryptoJS.enc.Hex.parse(data);conststr=CryptoJS.enc.Base64.stringify(encryptedHexStr);constdecrypt=CryptoJS.AES.decrypt(str,SECRET_KEY,{iv:SECRET_IV,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});constdecryptedStr=decrypt.toString(CryptoJS.enc.Utf8);returndecryptedStr.toString();}複製代碼

在存儲數據及獲取數據中進行使用:

這裡我們主要看下進行加密和解密部分,部分方法在下面代碼段中並未展示,請注意,不能直接運行。

constconfig={type:'localStorage',//本地存儲類型sessionStorageprefix:'SDF_0.0.1',//名稱前綴建議:項目名+項目版本expire:1,//過期時間單位:秒isEncrypt:true//默認加密為了調試方便,開發過程中可以不加密}//設置setStorageexportconstsetStorage=(key,value,expire=0)=>{if(value===''||value===null||value===undefined){value=null;}if(isNaN(expire)||expire<0)thrownewError("Expiremustbeanumber");expire=(expire?expire:config.expire)*1000;letdata={value:value,//存儲值time:Date.now(),//存值時間戳expire:expire//過期時間}//對存儲數據進行加密加密為可選配置constencryptString=config.isEncrypt?encrypt(JSON.stringify(data)):JSON.stringify(data);window[config.type].setItem(autoAddPrefix(key),encryptString);}//獲取getStorageexportconstgetStorage=(key)=>{key=autoAddPrefix(key);//key不存在判斷if(!window[config.type].getItem(key)||JSON.stringify(window[config.type].getItem(key))==='null'){returnnull;}//對存儲數據進行解密conststorage=config.isEncrypt?JSON.parse(decrypt(window[config.type].getItem(key))):JSON.parse(window[config.type].getItem(key));letnowTime=Date.now();//過期刪除if(storage.expire&&config.expire*6000<(nowTime-storage.time)){removeStorage(key);returnnull;}else{//持續使用時會自動續期setStorage(autoRemovePrefix(key),storage.value);returnstorage.value;}}複製代碼使用

使用的時候你可以通過import按需引入,也可以掛載到全局上使用,一般建議少用全局方式或全局變量,為後來接手項目繼續開發維護的人,追查代碼留條便捷之路!不要為了封裝而封裝,儘可能基於項目需求和後續的通用,以及使用上的便捷。比如獲取全部存儲變量,如果你項目上都未曾用到過,倒不如刪減掉,留着過年也不見得有多香,不如為減小體積做點貢獻!

import{isSupportStorage,hasStorage,setStorage,getStorage,getStorageKeys,getStorageForIndex,getStorageLength,removeStorage,getStorageAll,clearStorage}from'@/utils/storage'複製代碼完整代碼

該代碼已進一步完善,需要的可以直接進一步優化,也可以將可優化或可擴展的建議,留言說明,我會進一步迭代的。可以根據自己的需要刪除一些不用的方法,以減小文件大小。

/****title:storage.js*Author:Gaby*Email:xxx@126.com*Time:2022/6/117:30*last:2022/6/217:30*Desc:對存儲的簡單封裝*/importCryptoJSfrom'crypto-js';//十六位十六進制數作為密鑰constSECRET_KEY=CryptoJS.enc.Utf8.parse("3333e6e143439161");//十六位十六進制數作為密鑰偏移量constSECRET_IV=CryptoJS.enc.Utf8.parse("e3bbe7e3ba84431a");//類型window.localStorage,window.sessionStorage,constconfig={type:'localStorage',//本地存儲類型sessionStorageprefix:'SDF_0.0.1',//名稱前綴建議:項目名+項目版本expire:1,//過期時間單位:秒isEncrypt:true//默認加密為了調試方便,開發過程中可以不加密}//判斷是否支持StorageexportconstisSupportStorage=()=>{return(typeof(Storage)!=="undefined")?true:false}//設置setStorageexportconstsetStorage=(key,value,expire=0)=>{if(value===''||value===null||value===undefined){value=null;}if(isNaN(expire)||expire<0)thrownewError("Expiremustbeanumber");expire=(expire?expire:config.expire)*1000;letdata={value:value,//存儲值time:Date.now(),//存值時間戳expire:expire//過期時間}constencryptString=config.isEncrypt?encrypt(JSON.stringify(data)):JSON.stringify(data);window[config.type].setItem(autoAddPrefix(key),encryptString);}//獲取getStorageexportconstgetStorage=(key)=>{key=autoAddPrefix(key);//key不存在判斷if(!window[config.type].getItem(key)||JSON.stringify(window[config.type].getItem(key))==='null'){returnnull;}//優化持續使用中續期conststorage=config.isEncrypt?JSON.parse(decrypt(window[config.type].getItem(key))):JSON.parse(window[config.type].getItem(key));letnowTime=Date.now();//過期刪除if(storage.expire&&config.expire*6000<(nowTime-storage.time)){removeStorage(key);returnnull;}else{//未過期期間被調用則自動續期進行保活setStorage(autoRemovePrefix(key),storage.value);returnstorage.value;}}//是否存在hasStorageexportconsthasStorage=(key)=>{key=autoAddPrefix(key);letarr=getStorageAll().filter((item)=>{returnitem.key===key;})returnarr.length?true:false;}//獲取所有keyexportconstgetStorageKeys=()=>{letitems=getStorageAll()letkeys=[]for(letindex=0;index<items.length;index++){keys.push(items[index].key)}returnkeys}//根據索引獲取keyexportconstgetStorageForIndex=(index)=>{returnwindow[config.type].key(index)}//獲取localStorage長度exportconstgetStorageLength=()=>{returnwindow[config.type].length}//獲取全部getAllStorageexportconstgetStorageAll=()=>{letlen=window[config.type].length//獲取長度letarr=newArray()//定義數據集for(leti=0;i<len;i++){//獲取key索引從0開始letgetKey=window[config.type].key(i)//獲取key對應的值letgetVal=window[config.type].getItem(getKey)//放進數組arr[i]={'key':getKey,'val':getVal,}}returnarr}//刪除removeStorageexportconstremoveStorage=(key)=>{window[config.type].removeItem(autoAddPrefix(key));}//清空clearStorageexportconstclearStorage=()=>{window[config.type].clear();}//名稱前自動添加前綴constautoAddPrefix=(key)=>{constprefix=config.prefix?config.prefix+'_':'';returnprefix+key;}//移除已添加的前綴constautoRemovePrefix=(key)=>{constlen=config.prefix?config.prefix.length+1:'';returnkey.substr(len)//constprefix=config.prefix?config.prefix+'_':'';//returnprefix+key;}/***加密方法*@paramdata*@returns{string}*/constencrypt=(data)=>{if(typeofdata==="object"){try{data=JSON.stringify(data);}catch(error){console.log("encrypterror:",error);}}constdataHex=CryptoJS.enc.Utf8.parse(data);constencrypted=CryptoJS.AES.encrypt(dataHex,SECRET_KEY,{iv:SECRET_IV,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});returnencrypted.ciphertext.toString();}/***解密方法*@paramdata*@returns{string}*/constdecrypt=(data)=>{constencryptedHexStr=CryptoJS.enc.Hex.parse(data);conststr=CryptoJS.enc.Base64.stringify(encryptedHexStr);constdecrypt=CryptoJS.AES.decrypt(str,SECRET_KEY,{iv:SECRET_IV,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});constdecryptedStr=decrypt.toString(CryptoJS.enc.Utf8);returndecryptedStr.toString();}複製代碼

來自:Gaby

https://juejin.cn/post/7104301566857445412


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()