JavaScript AJAX 实现,演示如何将 Token 添加到 Authorization

以下是一个完整的原生 JavaScript AJAX 实现,演示如何将 Token 添加到 Authorization 头部的示例:

基础实现

html复制代码

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <title>AJAX Token 示例</title>
  <script>
    // 获取当前用户的 Token(实际应用中从安全存储获取)
    function getAuthToken() {
      // 实际项目中应从 localStorage/cookie 获取
      return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
    }

    // 发送带 Token 的 AJAX 请求
    function fetchDataWithToken() {
      const xhr = new XMLHttpRequest();
      const url = 'https://api.example.com/protected-data';
      const token = getAuthToken();
      
      // 配置请求
      xhr.open('GET', url, true);
      
      // 设置 Authorization 头部
      xhr.setRequestHeader('Authorization', `Bearer ${token}`);
      xhr.setRequestHeader('Content-Type', 'application/json');
      
      // 处理响应
      xhr.onreadystatechange = function() {
        if (xhr.readyState === XMLHttpRequest.DONE) {
          const status = xhr.status;
          
          if (status >= 200 && status < 300) {
            console.log('响应数据:', JSON.parse(xhr.responseText));
            document.getElementById('result').innerText = 
              `请求成功: ${xhr.responseText}`;
          } else {
            console.error('请求失败:', xhr.statusText);
            document.getElementById('result').innerText = 
              `错误 ${xhr.status}: ${xhr.statusText}`;
          }
        }
      };
      
      // 错误处理
      xhr.onerror = function() {
        console.error('网络错误');
        document.getElementById('result').innerText = '网络请求失败';
      };
      
      // 发送请求
      xhr.send();
    }
  </script>
</head>
<body>
  <h1>AJAX Token 示例</h1>
  <button onclick="fetchDataWithToken()">获取受保护数据</button>
  <div id="result" style="margin-top: 20px;"></div>
</body>
</html>

封装为可重用模块

javascript复制代码

javascript 复制代码
// ajaxWithAuth.js

const API_BASE_URL = 'https://api.example.com';

export default {
  /**
   * 发送带认证的 AJAX 请求
   * @param {string} method - HTTP 方法 (GET, POST, PUT, DELETE)
   * @param {string} endpoint - API 端点
   * @param {object} [data] - 请求数据
   * @returns {Promise} 返回 Promise
   */
  request(method, endpoint, data = null) {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      const url = `${API_BASE_URL}${endpoint}`;
      const token = this.getAuthToken();
      
      xhr.open(method, url, true);
      
      // 设置头部
      xhr.setRequestHeader('Authorization', `Bearer ${token}`);
      xhr.setRequestHeader('Content-Type', 'application/json');
      
      xhr.onload = function() {
        if (xhr.status >= 200 && xhr.status < 300) {
          try {
            resolve(JSON.parse(xhr.responseText));
          } catch (e) {
            resolve(xhr.responseText);
          }
        } else {
          reject({
            status: xhr.status,
            statusText: xhr.statusText
          });
        }
      };
      
      xhr.onerror = function() {
        reject({
          status: 0,
          statusText: '网络错误'
        });
      };
      
      xhr.send(data ? JSON.stringify(data) : null);
    });
  },
  
  // 获取认证 Token
  getAuthToken() {
    // 实际实现从安全存储获取
    return localStorage.getItem('authToken') || 
           sessionStorage.getItem('authToken') || 
           '';
  },
  
  // 封装常用方法
  get(endpoint) {
    return this.request('GET', endpoint);
  },
  
  post(endpoint, data) {
    return this.request('POST', endpoint, data);
  },
  
  put(endpoint, data) {
    return this.request('PUT', endpoint, data);
  },
  
  delete(endpoint) {
    return this.request('DELETE', endpoint);
  }
};

使用示例

html复制代码

html 复制代码
<script type="module">
  import ajax from './ajaxWithAuth.js';
  
  // 登录示例
  async function login() {
    try {
      const response = await ajax.post('/login', {
        username: 'user@example.com',
        password: 'securePassword123'
      });
      
      // 保存 token 到本地存储
      localStorage.setItem('authToken', response.token);
      console.log('登录成功');
    } catch (error) {
      console.error('登录失败:', error);
    }
  }
  
  // 获取受保护数据
  async function fetchProtectedData() {
    try {
      const userData = await ajax.get('/user/profile');
      console.log('用户数据:', userData);
      
      const orders = await ajax.get('/user/orders');
      console.log('订单数据:', orders);
    } catch (error) {
      console.error('请求失败:', error);
      
      // 处理 Token 过期
      if (error.status === 401) {
        console.log('Token过期,尝试刷新...');
        await refreshToken();
        return fetchProtectedData(); // 重试请求
      }
    }
  }
  
  // Token 刷新逻辑
  async function refreshToken() {
    try {
      const refreshToken = localStorage.getItem('refreshToken');
      const response = await ajax.post('/auth/refresh', { refreshToken });
      
      localStorage.setItem('authToken', response.accessToken);
      console.log('Token刷新成功');
    } catch (error) {
      console.error('刷新Token失败:', error);
      logout(); // 刷新失败则登出
    }
  }
  
  // 登出逻辑
  function logout() {
    localStorage.removeItem('authToken');
    localStorage.removeItem('refreshToken');
    console.log('用户已登出');
  }
  
  // 初始化
  document.addEventListener('DOMContentLoaded', () => {
    if (localStorage.getItem('authToken')) {
      fetchProtectedData();
    } else {
      document.getElementById('login-section').style.display = 'block';
    }
  });
</script>

安全增强措施

javascript复制代码

javascript 复制代码
// 安全增强版
const ajaxSecure = {
  // ... 基础代码同上 ...
  
  request(method, endpoint, data = null) {
    return new Promise((resolve, reject) => {
      // ... 同上 ...
      
      // 添加安全头部
      xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
      xhr.setRequestHeader('X-CSRF-Protection', this.getCSRFToken());
      
      // 添加请求时间戳防止缓存
      const timestamp = new Date().getTime();
      const finalUrl = endpoint.includes('?') 
        ? `${url}&_t=${timestamp}` 
        : `${url}?_t=${timestamp}`;
      
      xhr.open(method, finalUrl, true);
      
      // ... 其余代码 ...
    });
  },
  
  // 获取 CSRF Token
  getCSRFToken() {
    const cookieValue = document.cookie
      .split('; ')
      .find(row => row.startsWith('XSRF-TOKEN='))
      ?.split('=')[1];
    
    return cookieValue || '';
  },
  
  // 自动处理 Token 刷新
  async requestWithRefresh(method, endpoint, data) {
    try {
      return await this.request(method, endpoint, data);
    } catch (error) {
      if (error.status === 401 && !endpoint.includes('/auth/refresh')) {
        await this.refreshToken();
        return this.request(method, endpoint, data);
      }
      throw error;
    }
  },
  
  // 刷新 Token
  async refreshToken() {
    const refreshToken = localStorage.getItem('refreshToken');
    if (!refreshToken) throw new Error('无可用刷新令牌');
    
    try {
      const response = await this.request('POST', '/auth/refresh', {
        refreshToken
      });
      
      this.saveTokens(response);
      return true;
    } catch (error) {
      this.clearTokens();
      throw error;
    }
  },
  
  // 安全保存 Token
  saveTokens(authData) {
    // 使用安全存储方式
    localStorage.setItem('authToken', authData.accessToken);
    
    // 刷新令牌使用 HTTP Only Cookie 存储(由服务器设置)
    document.cookie = `refreshToken=${authData.refreshToken}; Secure; HttpOnly; SameSite=Strict; path=/`;
  },
  
  // 清除 Token
  clearTokens() {
    localStorage.removeItem('authToken');
    document.cookie = 'refreshToken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
  }
};

完整流程图

mermaid复制代码导出svg

最佳实践建议

  1. Token 存储安全

    javascript复制代码

    javascript 复制代码
    // 使用加密存储(浏览器扩展)
    async function secureSetItem(key, value) {
      if (window.crypto && window.crypto.subtle) {
        const encrypted = await encryptData(value);
        localStorage.setItem(key, encrypted);
      } else {
        // 回退方案:会话存储 + Base64
        sessionStorage.setItem(key, btoa(unescape(encodeURIComponent(value))));
      }
    }
  2. 添加请求签名

    javascript复制代码

    javascript 复制代码
    function signRequest(method, url, body) {
      const timestamp = Date.now();
      const nonce = Math.random().toString(36).substring(2, 12);
      const dataToSign = `${method}|${url}|${timestamp}|${nonce}|${body ? JSON.stringify(body) : ''}`;
      
      const hmac = CryptoJS.HmacSHA256(dataToSign, SECRET_KEY);
      return {
        'X-Signature': hmac.toString(CryptoJS.enc.Base64),
        'X-Timestamp': timestamp,
        'X-Nonce': nonce
      };
    }
  3. 双重 Token 验证

    javascript复制代码

    javascript 复制代码
    function setAuthHeaders(xhr) {
      const token = getAuthToken();
      xhr.setRequestHeader('Authorization', `Bearer ${token}`);
      
      // 添加设备指纹验证
      const deviceId = generateDeviceId();
      xhr.setRequestHeader('X-Device-ID', deviceId);
    }
    
    function generateDeviceId() {
      // 基于浏览器指纹生成唯一ID
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      ctx.fillText('ID', 10, 10);
      return canvas.toDataURL().hashCode();
    }

这个实现展示了如何在原生 AJAX 请求中添加认证 Token,并提供了企业级的安全增强措施。实际项目中,建议结合具体框架使用更高级的 HTTP 客户端(如 Axios),但理解底层原理对于处理特殊场景非常重要。

相关推荐
空中海3 小时前
01 React Native 基础、核心组件与布局体系
javascript·react native·react.js
前端之虎陈随易5 小时前
2年没用Nodejs了,Bun很香
linux·前端·javascript·vue.js·typescript
好运的阿财6 小时前
OpenClaw工具拆解之host_workspace_write+host_workspace_edit
前端·javascript·人工智能·机器学习·ai编程·openclaw·openclaw工具
XiYang-DING6 小时前
JavaScript
开发语言·javascript·ecmascript
空中海7 小时前
02 React Native状态、导航、数据流与设备能力
javascript·react native·react.js
空中海8 小时前
02 状态、Hooks、副作用与数据流
开发语言·javascript·ecmascript
空中海8 小时前
04 React Native工程化、质量、发布与生态选型
javascript·react native·react.js
杨超凡9 小时前
豆包收费了?我特么自己用“意念”搓了一个!
javascript
threelab10 小时前
Three.js 咖啡杯烟雾效果 | 三维可视化 / AI 提示词
开发语言·javascript·人工智能