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

发布于:2025-07-24 ⋅ 阅读:(28) ⋅ 点赞:(0)

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

基础实现

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复制代码

// 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复制代码

<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复制代码

// 安全增强版
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复制代码

    // 使用加密存储(浏览器扩展)
    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复制代码

    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复制代码

    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),但理解底层原理对于处理特殊场景非常重要。


网站公告

今日签到

点亮在社区的每一天
去签到