场景
由于项目创建之前后端设计不合理,导致详情页链接参数id为顺序序数(例:1,2,3…等等),安全系数非常低(虽然我们前端做了菜单权限、按钮权限、Api权限等等),现在要前端解决下浏览器链接/接口参数实现加密🤬🤬
注:前端链接加密与接口参数加密(get请求)类似
思路过程
一、设计格式
项目正常链接为
contract/draft/contract-draft-detail?id=26197&type=news&key=1667198460529
我们最终需要base64加密成这种(如果不想base64加密也可换成其他加密方式 👇
如:RSA加密、AES加密、MD5加密、SHA256加密以及国密)
contract/draft/contract-draft-detail?__params=eyJpZCI6MjYxOTcsInR5cGUiOiJuZXciLCJrZXkiOjE2NjcxOTg0NjA1Mjl9
要想将参数加密,我这里设计的是,将所有参数以对象的形式进行加密,如图
正常参数格式: `id=26197&type=news&key=1667198460529`,转化成对象:{id:26197,type:'news',key:'1667198460529'}
正常情况下,可直接 $route.query 获取参数对象,或 window.location.search 格式化获取,格式化函数见下方,
// URL参数转成对象exportfunctionurlPasseObj(url ='', isUrl){const result = url.split('?')[isUrl ?1:0]if(!result){return{}}const list = result.split('&')const obj ={}
list.map(item=>{if(item){const arr = item.split('=')const value = arr[1]
obj[arr[0]]= value ===undefined?'': value
}})return obj
}
- 加密后都放到一个参数中,这里我命名为 __params
二、加/解密方法实现
这里要注意的是base64加密正常是字符串(String类型)进行加密,对对象(Object)进行加密,需要通过JSON.parse进行转义成String类型
代码如下
/**
* base64(解密)
* @param {String} str 跳转参数为base64字符串
* @returns
*/exportconstdecryptBase64=function(str){// 添加decodeURIComponent解决其他特殊字符,如等号(=)会转成%3D,导致base64解密失败const decryptQuery = str ?JSON.parse(base64.decode(decodeURIComponent(str))):{}return decryptQuery
}/**
* base64(加密)
* @param {Object || String} param 跳转参数可以为对象或路径字符串
* @returns
*/exportconstencryptBase64=function(param){const encryptStr = base64.encode(JSON.stringify(param))||''return encryptStr
}
三、加密处理位置
- $route.push跳转处
通常咱们在Vue文件下通过 $router.push 进行路由跳转,跳转时需要对参数进行加密处理,这样跳转后的链接参数才会以加密的形式进行呈现。**有些人可能觉得单独写一个方法进行路由跳转,不从 $router.push跳转了**,但是我这个项目是中后期项目了,担心后面其他前端同事不熟悉项目,不知道加密这回事,所以我就在**Router.prototype.push** 直接进行修改了
代码如下:
// 在router/index.js中修改const originalPush =Router.prototype.push
Router.prototype.push=functionpush(location, onResolve, onReject){if(onResolve || onReject)returnoriginalPush.call(this, location, onResolve, onReject)// 解决空对象中含有{__ob__: Observer},深拷贝let newLocation =deepCopy(location)if(newLocation?.query && Object.keys(newLocation.query).length !==0){
newLocation = location.query?.__params ? location :{...location,query:{__params:encryptBase64(newLocation.query)}}}returnoriginalPush.call(this, newLocation).catch(err=> err)}
- 路由拦截请求(处理get请求参数) 在路由拦截器请求时,通常会处理一些逻辑。将token放入携带的请求头中等等,所以咱们要将get请求中参数在此处进行处理。get请求的参数会和浏览器链接一样,在url中,例: 所以我们处理方式与浏览器链接参数处理方式一样,具体实现方法如下,
request.interceptors.request.use(config=>{if(config.method ==='get'){
config.data =true// 判断是否有携带参数const isHash = config.url.includes('?')if(isHash){// 将url上的参数调整到params中,然后调整清除url上的参数// urlPasseObj方法在上方const urlParam =urlPasseObj(config.url,true)
config.params = config.params ?{...config.params,...urlParam }: urlParam
config.url = config.url.split('?')[0]}if(config.params){
config.params ={__params:encryptBase64(config.params)}}}return config
}, errorHandler)
注:在无参数时,config中不存在params属性,所以要初始化一下
四、解密处理位置
- 在App.vue中初始化一个变量来代替$router.query
watch:{'$route.query':{immediate:true,deep:true,handler(val){if(val.__params){const newQuery =decryptBase64(val?.__params)// 在vue原型上定义一个Vue.prototype.$route_query = newQuery
return}Vue.prototype.$route_query = val ||{}}}}
然后咱们只能将整个项目用到$route.query的地方,全局替换下了~~最后使用方式见下方
小结:
有可能会有人想问,为什么不把 $router.push也替换成全局,而只把 $route.query用一个变量替换?
因为如果不将$route.query替换成另一个变量( $route_query ),没办法获取到解密的参数(除非一个文件一个文件看着改,而不可以全局替换改)。我当时尝试在**路由守卫**和**路由钩子函数**里想要处理 $route.query中的参数,但是发现这个是一个只读的属性,不可以中途改变值。
不把 $router.push也替换成全局,是为了方便后续其他同事在开发时,不需要注意加密参数了,push自动格式化参数成加密。
版权归原作者 仙人掌上的刺猬 所有, 如有侵权,请联系我们删除。