aksk前端签名实现

需求:

页面和后台使用aksk进行签名校验,普通JSON参数签名没问题,但使用formData上传文件时签名总是无法通过后台校验

关键点:

1、浏览器在传递formData格式数据时会自动随机boundary,这样页面无法在请求发起前拿到随机boundary,造成前后台计算入参不一致

2、formData格式的数据是否可以直接用来计算其hash

解决方案:

1、针对随机boundary问题,通过手动指定解决

2、因为随机boundary问题,暂未找到直接对formData格式数据签名方式,构造其结构转二进制实现

关键代码:

1、将formData内容拆成两部分计算计算其二进制数据

js 复制代码
  const fields = {
    fileName: fileInfo.fileName,
    chunk: currentChunk + 1,
    chunks: totalChunks,
    uploadId: fileInfo.uploadId,
    fileType: fileType.value
  }
  const files = {
    file: chunk
  }
  const boundary = '----MyCustomBoundaryABC'

2、拼接二进制数据

js 复制代码
async function buildMultipartFormData(fields, files, boundary) {
  const CRLF = '\r\n'
  const encoder = new TextEncoder()
  const chunks = []

  const pushText = (text) => chunks.push(encoder.encode(text))

  // 普通字段
  for (const [name, value] of Object.entries(fields)) {
    pushText(`--${boundary}${CRLF}`)
    pushText(`Content-Disposition: form-data; name="${name}"${CRLF}${CRLF}`)
    pushText(`${value}${CRLF}`)
  }

  // 文件字段
  for (const [name, file] of Object.entries(files)) {
    const filename = file.name || 'blob'
    const mimeType = file.type || 'application/octet-stream'
    
    pushText(`--${boundary}${CRLF}`)
    pushText(`Content-Disposition: form-data; name="${name}"; filename="${filename}"${CRLF}`)
    pushText(`Content-Type: ${mimeType}${CRLF}${CRLF}`)
    
    const fileBuffer = new Uint8Array(await file.arrayBuffer())
    chunks.push(fileBuffer)
    pushText(CRLF)
  }

  // 结尾
  pushText(`--${boundary}--${CRLF}`)

  // 合并所有 Uint8Array 块
  const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0)
  const body = new Uint8Array(totalLength)
  let offset = 0
  for (const chunk of chunks) {
    body.set(chunk, offset)
    offset += chunk.length
  }
  return body
}

3、使用二进制数据进行签名

js 复制代码
  buildMultipartFormData(fields, files, boundary).then(async (bodyBinary) => {
    // 查看构造的内容(可选)
    const auth = await createAuth(bodyBinary)// 发送请求
    fetch('/cos/upload', {
      method: 'POST',
      headers: {
        'Content-Type': `multipart/form-data; boundary=${boundary}`,
        'Authorization': auth
      },
      body: bodyBinary
    })
  })

4、签名实现

ts 复制代码
import { SHA256, HmacSHA256, enc } from 'crypto-js';
function useAuth() {

  function hmacWithSHA256(message, secretKey) {
    // 计算 HMAC-SHA256
    const hmac = HmacSHA256(message, secretKey);

    // 返回十六进制字符串(小写)
    return hmac.toString(enc.Hex);
  }

  function generateRandomString(length = 16) {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let result = '';

    for (let i = 0; i < length; i++) {
      result += chars.charAt(Math.floor(Math.random() * chars.length));
    }

    return result;
  }
  
  async function hash256(data) {
    const str = typeof data === 'string' ? data : JSON.stringify(data);
    return SHA256(str).toString();
  }
  
  async function createAuth(bodyRaw) {
    const appid = 'app_demo'
    const access = 'ak_demo'
    const sk = 'sk_demo'
    const expiretime = Math.floor(Date.now() / 1000) + 10000
    const nonce = generateRandomString()
    let hashBody;
    if (bodyRaw instanceof Uint8Array) {
      hashBody = await calculateBinaryHash(bodyRaw)
    } else {
      hashBody = await hash256(typeof bodyRaw === 'string' ? bodyRaw : JSON.stringify(bodyRaw))
    }

    const signature = hmacWithSHA256(hashBody + expiretime + nonce, sk)
    const res = `appid=${appid},access=${access},signature=${signature},nonce=${nonce},expiretime=${expiretime}`
    return res
  }

  const calculateBinaryHash = async (binaryData: Uint8Array | ArrayBuffer): Promise<string> => {
    // 浏览器环境
    if (typeof window !== 'undefined' && crypto.subtle) {
      const hashBuffer = await crypto.subtle.digest('SHA-256', binaryData);
      const hashArray = Array.from(new Uint8Array(hashBuffer));
      return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    }
  };
  return {
    createAuth
  }
}

export { useAuth }
相关推荐
麦兜*3 分钟前
Spring Boot 集成Reactive Web 性能优化全栈技术方案,包含底层原理、压测方法论、参数调优
java·前端·spring boot·spring·spring cloud·性能优化·maven
Jinkxs4 分钟前
JavaScript性能优化实战技术
开发语言·javascript·性能优化
天上掉下来个程小白6 分钟前
MybatisPlus-06.核心功能-自定义SQL
java·spring boot·后端·sql·微服务·mybatisplus
知了一笑13 分钟前
独立开发第二周:构建、执行、规划
java·前端·后端
UI前端开发工作室1 小时前
数字孪生技术为UI前端提供新视角:产品性能的实时模拟与预测
大数据·前端
Sapphire~1 小时前
重学前端004 --- html 表单
前端·html
今天背单词了吗9801 小时前
算法学习笔记:17.蒙特卡洛算法 ——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·笔记·考研·算法·蒙特卡洛算法
Dcs1 小时前
从 C 到 Rust:一位开发者的 `tmux` 全面移植之旅
java
Maybyy1 小时前
力扣242.有效的字母异位词
java·javascript·leetcode
遇到困难睡大觉哈哈1 小时前
CSS中的Element语法
前端·css