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

文章目录

接口请求封装

请求拦截器

请求前

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

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,无法感知到文件更新

协商缓存

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

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

相关推荐
EndingCoder2 分钟前
类的继承和多态
linux·运维·前端·javascript·ubuntu·typescript
Zero_Era2 分钟前
高性价比安全MCU——LKT6830C
单片机·嵌入式硬件·安全
用户47949283569153 分钟前
React 终于出手了:彻底终结 useEffect 的"闭包陷阱"
前端·javascript·react.js
程序员猫哥9 分钟前
前端开发,一句话生成网站
前端
专注前端30年22 分钟前
【PHP开发与安全防护实战】性能调优手册
android·安全·php
Younglina30 分钟前
一个纯前端的网站集合管理工具
前端·vue.js·chrome
木头程序员31 分钟前
前端(包含HTML/JavaScript/DOM/BOM/jQuery)基础-暴力复习篇
开发语言·前端·javascript·ecmascript·es6·jquery·html5
卖火箭的小男孩33 分钟前
# Flutter Provider 状态管理完全指南
前端
小雨青年33 分钟前
鸿蒙 HarmonyOS 6|ArkUI(01):从框架认知到项目骨架
前端
Null15536 分钟前
浏览器唤起本地桌面应用(基础版)
前端·浏览器