# 实现请求可配置是否加密接口

​ 毫无疑问,系统数据安全以及防止接口被随意调用破解,接口请求参数及返回数据进行加密显得尤为重要。那如何实现呢?

​ 既然涉及到了请求数据加密,以及相应数据加密,那当然需要后端配合啦。具体逻辑

前端将请求数据加密发送请求

——》

后端根据前端加密对应的解密逻辑对数据进行解密后执行相应操作,将数据加密,给前端响应

——》

前端根据后端加密对应的解密逻辑对数据进行解密后执行相应操作

上代码

# 第一步:安装 crypto-js

npm install crypto-js@^4.0.0 -S

# 第二步:新建文件,定义好加密及解密方法

var CryptoJS = require("crypto-js");

const config = {
  // 秘钥   可自定义
  secret: "关注微信公众号【爆米花小布】不迷路",
}

//对象加密方法
export function encryptObj(reqData) {
  return CryptoJS.AES.encrypt(JSON.stringify(reqData), config.secret).toString()
}


//对象解密方法
export function decryptObj(resData) {
  const bytes = CryptoJS.AES.decrypt(resData, config.secret)
  return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
}

//字符串加密方法
export function encryptStr(reqData) {
  return CryptoJS.AES.encrypt(reqData + "", config.secret).toString()
}


//字符串解密
export function decryptStr(resData) {
  const bytes = CryptoJS.AES.decrypt(resData, config.secret)
  return bytes.toString(CryptoJS.enc.Utf8);
}

# 第三步:axios配置请求拦截及响应拦截中添加逻辑

import axios from "axios"
import Message from "@/plugins/reset-message.js"
import store from "@/store"

// 创建axios实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_URL,
  timeout: process.env.VUE_APP_TIME_OUT // 请求超时时间
})

// request拦截器
service.interceptors.request.use(
  config => {
    const token = getToken()
    if (token) {
      config.headers.Authorization = "Bearer " + token
    }
      
    // 如果存在config.data 则为对象加密方式
    if (config.data) {
      config.data = {
        encryptData: encryptObj(config.data)
      }
    }
    // 如果为params 由于此时参数是挂在url后面的,因此不能整个对象一起加密,得单独对有值的加密
    if (config.params) {
      for (const key in config.params) {
        if (config.params[key] !== "" && config.params[key] !== null && config.params[key] !== undefined) {
          config.params[key] = encryptStr(config.params[key])
        }
      }
    } 
    return config
  },
  error => {
    Promise.reject(error)
  }
)

// respone拦截器
service.interceptors.response.use(
  async response => {
    const res = response.data
    // 对后端返回的数据进行解密
    res.data = decryptObj(res.data)
      
    // 如果自定义代码不是200,则判断为错误。
    if (response.status !== 200) {
      Message({
        message: res.message || "Error",
        type: "error",
        duration: 5 * 1000
      })
      return Promise.reject(new Error(res.message || "Error"))
    } else {
      // code为200 或 返回的是文件流 直接返回
      if (res.code === 200 || response.config.responseType === "blob") {
        return res
      }
      Message({
        message: res.message || "Error",
        type: "error",
        duration: 5 * 1000
      })
      if (res.code === 401) {
        await store.dispatch("user/logout")
        window.location.reload()
      }
      return Promise.reject(new Error(res.message || "Error"))
    }
  },
  error => {
    if (error.name === "AxiosError") {
      Message.error(error.message)
    }
    return Promise.reject(new Error(error))
  }
)

export default service

上述处理后,要求后端也进行相应处理,都处理后 此时所有接口都已经进行了加密处理,但是存在个问题——部分以formdata方式请求的接口是无法进行加密的(可能可以加密,我不知道而已),因此需要能配置接口是否需要加密

# 第四步:配置接口需要加密

/**
 * @description: 登录
 * @param {*}
 * @return {*}
 * @author: syx
 */
export function login(data = {}, config = {
  encryption: true
}) {
  return axiosRequest.post("/xiaobu-admin/login", data, config)
}

# 第五步:在axios请求配置加解密中添加条件

import axios from "axios"
import Message from "@/plugins/reset-message.js"
import store from "@/store"
// 系统配置 是否 加密    此处请看下一篇文章 配置全局变量  
const encryption = window.configObj.encryption || process.env.VUE_APP_ENCRYPTION 
// 创建axios实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_URL,
  timeout: process.env.VUE_APP_TIME_OUT // 请求超时时间
})

// request拦截器
service.interceptors.request.use(
  config => {
    const token = getToken()
    if (token) {
      config.headers.Authorization = "Bearer " + token
    }
      
    // 不是formData格式数据 且 返回类型不是 blob 文件流  才进行加密
    const canEncryption = !((config.data instanceof FormData) || (config.responseType&&config.responseType.toLocaleLowerCase() === "blob"))
    //加密接口  系统配置可加密  且接口配置也加密 才进行加密
    if (encryption === "1" && config.encryption && canEncryption) {
      // 与后端约定,在请求头添加是否加密标识符
      config.headers.encrypt = "1"
      if (config.data) {
        config.data = {
          encryptData: encryptObj(config.data)
        }
      }
      if (config.params) {
        for (const key in config.params) {
          if (config.params[key] !== "" && config.params[key] !== null && config.params[key] !== undefined) {
            config.params[key] = encryptStr(config.params[key])
          }
        }
      }
    } 
    return config
  },
  error => {
    Promise.reject(error)
  }
)

// respone拦截器
service.interceptors.response.use(
  async response => {
    const res = response.data
    //如果系统配置可加密  且接口配置可加密  且返回数据类型不是文件流
    if (encryption === "1" && response.config.encryption && response.config.responseType !== "blob") {
      res.data = decryptObj(res.data)
    }
      
    // 如果自定义代码不是200,则判断为错误。
    if (response.status !== 200) {
      Message({
        message: res.message || "Error",
        type: "error",
        duration: 5 * 1000
      })
      return Promise.reject(new Error(res.message || "Error"))
    } else {
      // code为200 或 返回的是文件流 直接返回
      if (res.code === 200 || response.config.responseType === "blob") {
        return res
      }
      Message({
        message: res.message || "Error",
        type: "error",
        duration: 5 * 1000
      })
      if (res.code === 401) {
        await store.dispatch("user/logout")
        window.location.reload()
      }
      return Promise.reject(new Error(res.message || "Error"))
    }
  },
  error => {
    if (error.name === "AxiosError") {
      Message.error(error.message)
    }
    return Promise.reject(new Error(error))
  }
)

export default service
  

WARNING

FormData请求格式不支持加密。

这是需要前后端配合约定的优化,提前与后端约定好标识符。

如果对于大多数接口都需要进行加密的,可默认全部加密,对部分接口进行配置不加密,反之则同理。

# 总结

​ 此篇文章为axios请求优化其二,关于对接口进行加密处理的配置,关注微信公众号【爆米花小布】不迷路。如有问题,可私信。

史上最牛逼的axios请求优化 (opens new window)