JavaScript AJAX 实现,演示如何将 Token 添加到 Authorization
以下是一个完整的原生 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.jsconst 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);});},// 获取认证 TokengetAuthToken() {// 实际实现从安全存储获取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 TokengetCSRFToken() {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;}},// 刷新 Tokenasync 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;}},// 安全保存 TokensaveTokens(authData) {// 使用安全存储方式localStorage.setItem('authToken', authData.accessToken);// 刷新令牌使用 HTTP Only Cookie 存储(由服务器设置)document.cookie = `refreshToken=${authData.refreshToken}; Secure; HttpOnly; SameSite=Strict; path=/`;},// 清除 TokenclearTokens() {localStorage.removeItem('authToken');document.cookie = 'refreshToken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';}
};
完整流程图
mermaid复制代码导出svg
最佳实践建议
Token 存储安全
javascript复制代码
// 使用加密存储(浏览器扩展) async function secureSetItem(key, value) {if (window.crypto && window.crypto.subtle) {const encrypted = await encryptData(value);localStorage.setItem(key, encrypted);} else {// 回退方案:会话存储 + Base64sessionStorage.setItem(key, btoa(unescape(encodeURIComponent(value))));} }
添加请求签名
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}; }
双重 Token 验证
javascript复制代码
function setAuthHeaders(xhr) {const token = getAuthToken();xhr.setRequestHeader('Authorization', `Bearer ${token}`);// 添加设备指纹验证const deviceId = generateDeviceId();xhr.setRequestHeader('X-Device-ID', deviceId); }function generateDeviceId() {// 基于浏览器指纹生成唯一IDconst canvas = document.createElement('canvas');const ctx = canvas.getContext('2d');ctx.fillText('ID', 10, 10);return canvas.toDataURL().hashCode(); }
这个实现展示了如何在原生 AJAX 请求中添加认证 Token,并提供了企业级的安全增强措施。实际项目中,建议结合具体框架使用更高级的 HTTP 客户端(如 Axios),但理解底层原理对于处理特殊场景非常重要。