前端接口安全与性能优化实战

文章目录

接口请求封装

请求拦截器

请求前

设置请求头防止重放攻击:

X-Timestamp 时间戳

后端代码

java 复制代码
/**
     * 验证时间戳有效性(例如5分钟内有效)
     */
    private boolean isValidTimestamp(String timestampStr, HttpServletResponse response) throws IOException {
        if (timestampStr == null) {
            sendErrorResponse(response, "缺少时间戳");
            return false;
        }

        try {
            long timestamp = Long.parseLong(timestampStr);
            long currentTime = System.currentTimeMillis();
            long timeDiff = Math.abs(currentTime - timestamp);

            if (timeDiff > 5 * 60 * 1000) {
                sendErrorResponse(response, "请求已过期");
                return false;
            }
        } catch (NumberFormatException e) {
            sendErrorResponse(response, "时间戳格式错误");
            return false;
        }

        return true;
    }
X-Nonce 长度16位的随机字符串
java 复制代码
/**
     * 验证随机字符串有效性
     *
     * @param nonce
     * @param response
     * @return
     * @throws IOException
     */
    private boolean isValidNonce(String nonce, HttpServletResponse response) throws IOException {
        if (nonce == null || nonce.isEmpty()) {
            sendErrorResponse(response, "缺少随机字符串");
            return false;
        }

        // 验证nonce是否已使用(防止重放攻击)
        String nonceKey = "nonce:" + nonce;
        Boolean isNewNonce = redisTemplate.opsForValue().setIfAbsent(nonceKey, "1", 5, TimeUnit.MINUTES);
        if (Boolean.FALSE.equals(isNewNonce)) {
            sendErrorResponse(response, "重复的请求");
            return false;
        }
        return true;
    }
X-Signateure api请求签名
js 复制代码
// 生成请求签名
export function generateSignature(secretKey, method, url, timestamp, nonce, body = '') {
  const key = String(secretKey || '')
  const httpMethod = String(method || '').toUpperCase()
  const requestUrl = String(url || '')
  const time = String(timestamp || '')
  const nonceStr = String(nonce || '')

  const finalUrl = processUrl(requestUrl)
  const bodyStr = serializeBody(httpMethod, body)
  const data = buildSignatureData(httpMethod, finalUrl, time, nonceStr, bodyStr)

  console.log('Signature data:', data)
  const signature = sha256.hmac(key, data)
  console.log('Generated signature:', signature)
  return signature
}
java 复制代码
/**
 * 验证签名有效性
 *
 * @param signature
 * @param response
 * @param dataForSigning
 * @return
 * @throws IOException
 */
private boolean isValidSignature(String signature, HttpServletResponse response, String dataForSigning) throws IOException {
    if (signature == null || signature.isEmpty()) {
        sendErrorResponse(response, "缺少签名");
        return true;
    }

    // 构造待签名数据(不包含请求体)
    log.info("Data for signing (multipart): {}", dataForSigning);

    // 计算签名
    String computedSignature = computeSignature(dataForSigning, clientSecret);
    log.info("Computed signature: {}", computedSignature);
    log.info("Received signature: {}", signature);

    // 验证签名
    if (!computedSignature.equals(signature)) {
        sendErrorResponse(response, "无效的签名");
        return true;
    }
    return false;
}
java 复制代码
/**
 * 计算HMAC-SHA256签名
 */
private String computeSignature(String data, String secret) {
    try {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        mac.init(secretKeySpec);
        byte[] signatureBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return bytesToHex(signatureBytes);
    } catch (Exception e) {
        log.error("签名计算失败", e);
        throw new RuntimeException("签名计算失败", e);
    }
}
X-Client-ID 客户端id

​ 如果token存在并且请求路径不是白名单则添加认证头Authorization

​ 为每个请求生成唯一标识

js 复制代码
const requestId = `${config.url}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
config.requestId = requestId

​ 只有当配置中 showLoading 不为 false 时才显示 loading或者进度条

​ 记录请求开始时间

请求失败

​ 请求错误时关闭 loading或者进度条

​ 关闭对应请求的 loading或者进度条

​ ...

响应拦截器

成功

​ 响应成功时关闭对应请求的 loading或者进度条

​ 计算接口耗时

​ 如果response.data instanceof Blob return resopnse.data

​ 如果业务状态码表示成功则返回response.data.data

​ 其他情况:业务码[code]?.()

失败

​ 响应错误时关闭对应请求的 loading或者进度条

​ 计算接口耗时(即使出错也记录)

​ 处理401状态码清除token跳转到登录页或刷新token,拿短token换长token

​ 处理其他错误:httpErrorCode[status]?.()

缓存接口数据防止重复请求

职责链模式

缓存模块-》防止重复提交模块-》请求模块》请求处理模块

用时间切片优化项目速度

webworker执行耗时任务

注意事项

vue2项目改用vite

删除node_modules

删除package.json中的开发依赖

删除babel配置文件

删除package.json中的eslint配置

删除postcss配置

从资源加载优化角度加速项目

浏览器缓存的两种机制

强缓存

优点:一定期限内,根本不用向服务器询问,一定是拿到缓存。速度最快

缺点:如何不配合hash,无法感知到文件更新

协商缓存

优点:能够保证每次前端打包后丢上服务器资源一定更新

缺点:只要文件是新放的,即使文件内容没变也不缓存

相关推荐
大布布将军2 小时前
《前端九阴真经》
前端·javascript·经验分享·程序人生·前端框架·1024程序员节
幸运小圣2 小时前
for...of vs for 循环全面对比【前端JS】
开发语言·前端·javascript
用户9545156811622 小时前
实际开发中 | 与 || 的使用方法及组件封装方案解析
前端
得帆云低代码2 小时前
COC Asia 2025|得帆云 ETL:顺应 Hive 新特性,重塑数据管道的未来
前端
十字路口的火丁3 小时前
前端开发如何灵活使用 css 变量
前端
kyle~3 小时前
计算机网络---安全外壳协议(SSH,Secure Shell)
运维·计算机网络·安全·ssh
_志哥_3 小时前
深度解析:解决 backdrop-filter 与 border-radius 的圆角漏光问题
前端·javascript·html
南囝coding3 小时前
100% 用 AI 做完一个新项目,从 Plan 到 Finished 我学到了这些
前端·后端
qiao若huan喜3 小时前
10、webgl 基本概念 + 坐标系统 + 立方体
前端·javascript·信息可视化·webgl