最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue?項(xiàng)目優(yōu)雅的對(duì)url參數(shù)加密詳解

 更新時(shí)間:2022年10月27日 16:36:26   作者:WujieLi  
這篇文章主要為大家介紹了vue?項(xiàng)目優(yōu)雅的對(duì)url參數(shù)加密詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

實(shí)現(xiàn)方案:stringifyQuery 和 parseQuery

近期因?yàn)楣緝?nèi)部的安全檢查,說我們現(xiàn)在的系統(tǒng)中參數(shù)是明文的,包括給后端請(qǐng)求的參數(shù)和前端頁面跳轉(zhuǎn)攜帶的參數(shù),因?yàn)槭枪緝?nèi)部使用的系統(tǒng),在安全性方面的設(shè)計(jì)考慮確實(shí)不夠充分

對(duì)于參數(shù)的加密和解密很好實(shí)現(xiàn),直接采用常用的 AES 算法,前后端定義好通用的密鑰和加解密方式就好,前端加解密這里主要使用到 crypto-js 這個(gè)工具包,再通過一個(gè)類簡單封裝一下加解密的算法即可

// src\utils\cipher.ts
import { encrypt, decrypt } from 'crypto-js/aes'
import { parse } from 'crypto-js/enc-utf8'
import pkcs7 from 'crypto-js/pad-pkcs7'
import ECB from 'crypto-js/mode-ecb'
import UTF8 from 'crypto-js/enc-utf8'
// 注意 key 和 iv 至少都需要 16 位
const AES_KEY = '1111111111000000'
const AES_IV = '0000001111111111'
export class AesEncryption {
  private key
  private iv
  constructor(key = AES_KEY, iv = AES_IV) {
    this.key = parse(key)
    this.iv = parse(iv)
  }
  get getOptions() {
    return {
      mode: ECB,
      padding: pkcs7,
      iv: this.iv,
    }
  }
  encryptByAES(text: string) {
    return encrypt(text, this.key, this.getOptions).toString()
  }
  decryptByAES(text: string) {
    return decrypt(text, this.key, this.getOptions).toString(UTF8)
  }
}

對(duì)于前端頁面間跳轉(zhuǎn)攜帶參數(shù),我們項(xiàng)目使用的都是 vue-router 的 query 來攜帶參數(shù),但是有那么多頁面跳轉(zhuǎn)的地方,不可能都手動(dòng)添加加解密方法處理吧,工作量大不說,萬一漏改一個(gè)就可能導(dǎo)致整個(gè)頁面無法加載了,這鍋可不能背

首先想到的方法是在路由守衛(wèi) beforeEach 中對(duì)參數(shù)進(jìn)行加密,然后在 afterEach 守衛(wèi)中對(duì)參數(shù)進(jìn)行解密,但是這個(gè)想法在 beforeEach 中加密就無法實(shí)現(xiàn)。原因是 beforeEach(to, from, next) 的第三個(gè)參數(shù) next 函數(shù)中,如果參數(shù)是路由對(duì)象,會(huì)導(dǎo)致跳轉(zhuǎn)死循環(huán)

接下來經(jīng)過幾個(gè)小時(shí)百思不得其解(摸魚)之后,最終在 API 參考 | Vue Router (vuejs.org) 找到這樣兩個(gè) API:stringifyQueryparseQuery,官網(wǎng)的定義如下

stringifyQuery:對(duì)查詢對(duì)象進(jìn)行字符串化的自定義實(shí)現(xiàn)。不應(yīng)該在前面加上 ?。應(yīng)該正確編碼查詢鍵和值

parseQuery:用于解析查詢的自定義實(shí)現(xiàn)。必須解碼查詢鍵和值

比如,官網(wǎng)建議如果想使用 qs 包來解析查詢,可以這樣配置

import qs from 'qs'
createRouter({
  // 其他配置...
  parseQuery: qs.parse,
  stringifyQuery: qs.stringify,
})

現(xiàn)在最終的解決方案就很明確了,自定義兩個(gè)參數(shù)加密、解密的方法,然后在 createRouter 中添加到 stringifyQueryparseQuery 這兩個(gè)方法就可以了,下面是詳細(xì)代碼

// src/router/helper/query.js
import { isArray, isNull, isUndefined } from 'lodash-es'
import { AesEncryption } from '@/utils/cipher'
import type {
  LocationQuery,
  LocationQueryRaw,
  LocationQueryValue,
} from 'vue-router'
const aes = new AesEncryption()
/**
 *
 * @description 解密:反序列化字符串參數(shù)
 */
export function stringifyQuery(obj: LocationQueryRaw): string {
  if (!obj) return ''
  const result = Object.keys(obj)
    .map((key) => {
      const value = obj[key]
      if (isUndefined(value)) return ''
      if (isNull(value)) return key
      if (isArray(value)) {
        const resArray: string[] = []
        value.forEach((item) => {
          if (isUndefined(item)) return
          if (isNull(item)) {
            resArray.push(key)
          } else {
            resArray.push(key + '=' + item)
          }
        })
        return resArray.join('&')
      }
      return `${key}=${value}`
    })
    .filter((x) => x.length > 0)
    .join('&')
  return result ? `?${aes.encryptByAES(result)}` : ''
}
/**
 *
 * @description 解密:反序列化字符串參數(shù)
 */
export function parseQuery(query: string): LocationQuery {
  const res: LocationQuery = {}
  query = query.trim().replace(/^(\?|#|&)/, '')
  if (!query) return res
  query = aes.decryptByAES(query)
  query.split('&').forEach((param) => {
    const parts = param.replace(/\+/g, ' ').split('=')
    const key = parts.shift()
    const val = parts.length > 0 ? parts.join('=') : null
    if (!isUndefined(key)) {
      if (isUndefined(res[key])) {
        res[key] = val
      } else if (isArray(res[key])) {
        ;(res[key] as LocationQueryValue[]).push(val)
      } else {
        res[key] = [res[key] as LocationQueryValue, val]
      }
    }
  })
  return res
}
// src/router/index.js
// 創(chuàng)建路由使用加解密方法
import { parseQuery, stringifyQuery } from './helper/query'
export const router = createRouter({
  // 創(chuàng)建一個(gè) hash 歷史記錄。
  history: createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH),
  routes: basicRoutes,
  scrollBehavior: () => ({ left: 0, top: 0 }),
  stringifyQuery, // 序列化query參數(shù)
  parseQuery, // 反序列化query參數(shù)
})

加密的效果如下,我也在 github 上傳了加密方式的 demo,可以直接下載體驗(yàn)一下

更進(jìn)一步:相關(guān)實(shí)現(xiàn)原理

在實(shí)現(xiàn)完這兩個(gè)功能之后,我突然想翻一下 Vue Router 的源碼,看一下 stringifyQueryparseQuery 的實(shí)現(xiàn)原理,避免以后遇到類似的問題再抓瞎

打開 Vue Router@4的源碼,整個(gè)項(xiàng)目是用 pnpm 管理 monorepo 的方式組織,通過 rollup.config.js 中定義的 input 入口可以知道,所有的方法都通過 packages/router/src/index.ts 導(dǎo)出

首先先看初始化路由實(shí)例的 createRouter 方法,這個(gè)方法主要做了這么幾件事

  • 通過 createRouterMatcher 方法,根據(jù)路由配置列表創(chuàng)建 matcher,返回 5 個(gè)操作 matcher 方法。matcher 可以理解為路由頁面匹配器,包含路由所有信息和 crud 操作方法
  • 定義三個(gè)路由守衛(wèi):beforeEach、beforeResolve、afterEach
  • 聲明當(dāng)前路由 currentRoute,對(duì) url 參數(shù) paramas 進(jìn)行編碼處理
  • 添加路由的各種操作方法,最后返回一個(gè) router 對(duì)象

一個(gè)簡化版本的 createRouter 方法如下所示,前文使用到的 stringifyQueryparseQuery 都是在這個(gè)方法中加載

export function createRouter(options: RouterOptions): Router {
  // 創(chuàng)建路由匹配器 matcher
  const matcher = createRouterMatcher(options.routes, options)
  // ! 使用到的 stringifyQuery 和 parseQuery
  const parseQuery = options.parseQuery || originalParseQuery
  const stringifyQuery = options.stringifyQuery || originalStringifyQuery
  // ! 路由守衛(wèi)定義
  const beforeGuards = useCallbacks<NavigationGuardWithThis<undefined>>()
  const beforeResolveGuards = useCallbacks<NavigationGuardWithThis<undefined>>()
  const afterGuards = useCallbacks<NavigationHookAfter>()
  // 聲明當(dāng)前路由
  const currentRoute = shallowRef<RouteLocationNormalizedLoaded>(
    START_LOCATION_NORMALIZED
  )
  let pendingLocation: RouteLocation = START_LOCATION_NORMALIZED
  // leave the scrollRestoration if no scrollBehavior is provided
  if (isBrowser && options.scrollBehavior && 'scrollRestoration' in history) {
    history.scrollRestoration = 'manual'
  }
  // url 參數(shù)進(jìn)行編碼處理
  const normalizeParams = applyToParams.bind(
    null,
    (paramValue) => '' + paramValue
  )
  const encodeParams = applyToParams.bind(null, encodeParam)
  const decodeParams: (params: RouteParams | undefined) => RouteParams =
        applyToParams.bind(null, decode) 
  }

從創(chuàng)建路由實(shí)例來看, stringifyQueryparseQuery 兩個(gè)參數(shù)如果沒有自定義傳入的情況下,會(huì)使用 vue-router 默認(rèn)的解析函數(shù)

默認(rèn)的 stringifyQuery 函數(shù)用于把參數(shù)由對(duì)象形式轉(zhuǎn)換為字符串連接形式,主要流程

  • 循環(huán)參數(shù) query 對(duì)象
  • 特殊處理參數(shù)為 null 的情況,參數(shù)值為 null 的情況會(huì)拼接在 url 鏈接中但是沒有值,而參數(shù)值為 undefined 則會(huì)直接忽略
  • 將對(duì)象轉(zhuǎn)化為數(shù)組,并且對(duì)每個(gè)對(duì)象的值進(jìn)行 encoded 處理
  • 將數(shù)組拼接為字符串參數(shù)
// vue-router 默認(rèn)的序列化 query 參數(shù)的函數(shù)
export function stringifyQuery(query: LocationQueryRaw): string {
  let search = ''
  for (let key in query) {
    const value = query[key]
    key = encodeQueryKey(key)
    // 處理參數(shù)為 null 的情況
    if (value == null) {
      if (value !== undefined) {
        search += (search.length ? '&' : '') + key
      }
      continue
    }
    // 將參數(shù)處理為數(shù)組,便于后續(xù)統(tǒng)一遍歷處理
    const values: LocationQueryValueRaw[] = isArray(value)
    ? value.map(v => v && encodeQueryValue(v))
    : [value && encodeQueryValue(value)]
    values.forEach(value => {
      // 跳過參數(shù)為 undefined 的情況,只拼接有值的參數(shù)
      if (value !== undefined) {
        search += (search.length ? '&' : '') + key
        if (value != null) search += '=' + value
      }
    })
  }
  return search
}
// 示例參數(shù),如下參數(shù)會(huì)被轉(zhuǎn)換為:name=wujieli&age=12&address
// query: {
//   id: undefined,
//   name: 'wujieli',
//   age: 12,
//   address: null,
// },

默認(rèn)的 parseQuery 函數(shù)用來將字符串參數(shù)解析為對(duì)象,主要流程

  • 排除空字符串和字符串前的 "?"
  • 對(duì)字符串用 "&" 分割,遍歷分割后的數(shù)組
  • 根據(jù) "=" 截取參數(shù)的 key 和 value,并對(duì) key 和 value 做 decode 處理
  • 處理 key 重復(fù)存在的情況,如果 key 對(duì)應(yīng) value 是數(shù)組,就把 value 添加進(jìn)數(shù)組中,否則就覆蓋前一個(gè) value
// vue-router 默認(rèn)的序列化 query 參數(shù)的函數(shù)
export function parseQuery(search: string): LocationQuery {
  const query: LocationQuery = {}
  // 因?yàn)橐獙?duì)字符串進(jìn)行 split('&') 操作,所以優(yōu)先排除空字符串
  if (search === '' || search === '?') return query
  // 排除解析參數(shù)前的 ?
  const hasLeadingIM = search[0] === '?'
  const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&')
  for (let i = 0; i < searchParams.length; ++i) {
    // 根據(jù) = 截取參數(shù)的 key 和 value,并做 decode 處理
    const searchParam = searchParams[i].replace(PLUS_RE, ' ')
    const eqPos = searchParam.indexOf('=')
    const key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos))
    const value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1))
    // 處理 key 重復(fù)存在的情況
    if (key in query) {
      // an extra variable for ts types
      let currentValue = query[key]
      if (!isArray(currentValue)) {
        currentValue = query[key] = [currentValue]
      }
      // we force the modification
      ;(currentValue as LocationQueryValue[]).push(value)
    } else {
      query[key] = value
    }
  }
  return query
}

stringifyQuery 這個(gè)方法用在創(chuàng)建 router 實(shí)例時(shí)提供的 resolve 方法中用來生成 url,parseQuery 方法主要用在 router.pushrouter.replace 等方法中解析 url 攜帶的參數(shù)

// stringifyQuery 方法的使用
function resolve(
rawLocation: Readonly<RouteLocationRaw>,
 currentLocation?: RouteLocationNormalizedLoaded
): RouteLocation & { href: string } {
  // ...
  // 鏈接的完整 path,包括路由 path 和后面的完整參數(shù)
  const fullPath = stringifyURL(
    stringifyQuery,
    assign({}, rawLocation, {
      hash: encodeHash(hash),
      path: matchedRoute.path,
    })
  )
}
// parseQuery 方法會(huì)封裝在 locationAsObject 方法中使用
function locationAsObject(
to: RouteLocationRaw | RouteLocationNormalized
): Exclude<RouteLocationRaw, string> | RouteLocationNormalized {
  return typeof to === 'string'
    ? parseURL(parseQuery, to, currentRoute.value.path)
  : assign({}, to)
}

以上就是 stringifyQueryparseQuery 兩個(gè)方法的實(shí)現(xiàn)原理,可以看到源碼中對(duì)于參數(shù)的加密解密考慮的處理是更多的,其實(shí)也可以把兩個(gè)方法的源碼拷貝出來,加上加密、解密的方法然后覆蓋源碼即可,更多關(guān)于vue url 參數(shù)加密的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 一文教會(huì)你如何運(yùn)行vue項(xiàng)目

    一文教會(huì)你如何運(yùn)行vue項(xiàng)目

    最近因?yàn)楣卷?xiàng)目問題,開始學(xué)習(xí)vue,這篇文章主要給大家介紹了關(guān)于如何運(yùn)行vue項(xiàng)目的相關(guān)資料,文中還介紹了如何運(yùn)行別人的項(xiàng)目,需要的朋友可以參考下
    2022-06-06
  • Vue解讀之響應(yīng)式原理源碼剖析

    Vue解讀之響應(yīng)式原理源碼剖析

    Vue 最獨(dú)特的特性之一,是其非侵入性的響應(yīng)式系統(tǒng),下面這篇文章主要給大家介紹了關(guān)于Vue響應(yīng)式原理源碼剖析的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-10-10
  • 詳解如何優(yōu)雅的進(jìn)行Vue的狀態(tài)管理

    詳解如何優(yōu)雅的進(jìn)行Vue的狀態(tài)管理

    隨著項(xiàng)目的發(fā)展和復(fù)雜性的增加,對(duì) Vuex 進(jìn)行更深入的了解和使用就變得非常重要,本篇文章將帶您探索 Vuex 的進(jìn)階使用,包括模塊化、命名空間、getter 的高級(jí)用法等,需要的朋友可以參考下
    2023-09-09
  • 深入淺析Vue中mixin和extend的區(qū)別和使用場景

    深入淺析Vue中mixin和extend的區(qū)別和使用場景

    Vue中有兩個(gè)較為高級(jí)的靜態(tài)方法mixin和extend,接下來給大家介紹Vue中mixin和extend的區(qū)別和使用場景,感興趣的朋友一起看看吧
    2019-08-08
  • vue中echarts引入中國地圖的案例

    vue中echarts引入中國地圖的案例

    這篇文章主要介紹了vue中echarts引入中國地圖的案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-07-07
  • Vue watch原理源碼層深入講解

    Vue watch原理源碼層深入講解

    watch 是由用戶定義的數(shù)據(jù)監(jiān)聽,當(dāng)監(jiān)聽的屬性發(fā)生改變就會(huì)觸發(fā)回調(diào),這項(xiàng)配置在業(yè)務(wù)中是很常用。在面試時(shí),也是必問知識(shí)點(diǎn),一般會(huì)用作和 computed 進(jìn)行比較。那么本文就來帶大家從源碼理解 watch 的工作流程,以及依賴收集和深度監(jiān)聽的實(shí)現(xiàn)
    2022-10-10
  • vue 項(xiàng)目 iOS WKWebView 加載

    vue 項(xiàng)目 iOS WKWebView 加載

    這篇文章主要介紹了vue 項(xiàng)目 iOS WKWebView 加載問題,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-04-04
  • vue項(xiàng)目proxyTable配置小結(jié)

    vue項(xiàng)目proxyTable配置小結(jié)

    本文主要介紹了vue項(xiàng)目proxyTable配置小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • 使用Vue簡單實(shí)現(xiàn)一個(gè)上拉加載更多分頁組件

    使用Vue簡單實(shí)現(xiàn)一個(gè)上拉加載更多分頁組件

    上拉加載更多的分頁功能大家應(yīng)該都見過或者使用過了吧,那么有多少同學(xué)自己實(shí)現(xiàn)過嗎,本文我們來簡單實(shí)現(xiàn)一個(gè)上拉加載更多分頁組件吧
    2024-11-11
  • Vue3中的Fragment使用方法詳解

    Vue3中的Fragment使用方法詳解

    Fragment 是 Vue 3 中的新特性,允許一個(gè)組件模板返回多個(gè)根節(jié)點(diǎn),與傳統(tǒng)方式不同,不再需要一個(gè)額外的 DOM 元素來包裹所有內(nèi)容,本文將詳細(xì)介紹 Fragment 的概念、使用場景、優(yōu)點(diǎn)以及可能遇到的問題,需要的朋友可以參考下
    2024-08-08

最新評(píng)論

兴仁县| 乡宁县| 两当县| 蒙自县| 鄄城县| 搜索| 西畴县| 宁化县| 彩票| 渝中区| 黄平县| 临清市| 芷江| 昔阳县| 秭归县| 汉中市| 伽师县| 都兰县| 岢岚县| 婺源县| 罗定市| 临颍县| 车致| 合山市| 荣昌县| 长寿区| 云南省| 中宁县| 高阳县| 博兴县| 奉节县| 铜川市| 元江| 宁夏| 库伦旗| 建德市| 安远县| 磐安县| 增城市| 汉沽区| 龙里县|