Files
vue-beta/Frontend_API_Documentation.md
qq1244 3541ae7755 实现完整的登录系统和退出登录功能
- 新增登录、注册、修改密码页面,支持三种用户类型
- 实现路由守卫和权限控制系统
- 集成Axios拦截器处理认证和错误
- 添加统一错误页面处理
- 完善Header组件退出登录功能,清除本地数据并跳转登录页
- 使用Element Plus构建现代化UI界面
- 支持响应式设计和移动端适配
2025-06-21 11:21:19 +08:00

16 KiB
Raw Blame History

餐厅管理系统 - 前端API接口文档

📋 接口概述

本系统使用JWT Token进行身份认证。除了登录注册接口外所有API都需要在请求头中携带有效的JWT Token。

服务器地址: http://localhost:8080

🔐 认证机制

JWT Token使用方式

  • 获取Token: 通过登录接口获取
  • 使用Token: 在请求头中添加 Authorization: Bearer {token}
  • Token有效期: 24小时
  • 无需Token: 仅注册和登录接口

📱 认证相关接口

1. 用户注册

接口地址: POST /api/auth/register
是否需要Token:

请求参数:

{
    "accountName": "用户名3-20字符字母数字下划线",
    "accountPassword": "密码8-20字符至少包含字母和数字",
    "confirmPassword": "确认密码(需与密码相同)",
    "accountPhone": "手机号11位数字可选",
    "accountType": "账户类型admin/manager/customer",
    "accountGender": "性别male/female可选"
}

请求示例:

const registerData = {
    accountName: "zhangsan",
    accountPassword: "password123",
    confirmPassword: "password123",
    accountPhone: "13812345678",
    accountType: "customer",
    accountGender: "male"
};

const response = await fetch('http://localhost:8080/api/auth/register', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(registerData)
});

const result = await response.json();

响应示例:

{
    "code": 200,
    "msg": "成功",
    "data": {
        "token": null,
        "accountInfo": {
            "accountId": 1,
            "accountName": "zhangsan",
            "accountType": "customer",
            "accountPhone": "13812345678",
            "accountGender": "male",
            "accountImg": null
        }
    }
}

2. 用户登录

接口地址: POST /api/auth/login
是否需要Token:

请求参数:

{
    "accountName": "用户名",
    "accountPassword": "密码"
}

请求示例:

const loginData = {
    accountName: "zhangsan",
    accountPassword: "password123"
};

const response = await fetch('http://localhost:8080/api/auth/login', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(loginData)
});

const result = await response.json();

// 保存Token到本地存储
if (result.code === 200) {
    const jwtToken = result.data.token;
    localStorage.setItem('token', jwtToken);
    localStorage.setItem('userInfo', JSON.stringify(result.data.accountInfo));
}

响应示例:

{
    "code": 200,
    "msg": "成功",
    "data": {
        "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ6aGFuZ3NhbiIsImlhdCI6MTcwNDczNzgxLCJleHAiOjE3NTA1NjAxODF9...",
        "accountInfo": {
            "accountId": 1,
            "accountName": "zhangsan",
            "accountType": "customer",
            "accountPhone": "13812345678",
            "accountGender": "male",
            "accountImg": null
        }
    }
}

3. 修改密码

接口地址: PUT /api/auth/change-password
是否需要Token:

请求参数:

{
    "oldPassword": "当前密码",
    "newPassword": "新密码8-20字符至少包含字母和数字",
    "confirmPassword": "确认新密码"
}

请求示例:

// 获取存储的Token
const storedToken = localStorage.getItem('token');

const passwordData = {
    oldPassword: "password123",
    newPassword: "newpassword456",
    confirmPassword: "newpassword456"
};

// 构建请求头确保完整Token传递
const headers = {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${storedToken}`
};

const response = await fetch('http://localhost:8080/api/auth/change-password', {
    method: 'PUT',
    headers: headers,
    body: JSON.stringify(passwordData)
});

const result = await response.json();

响应示例:

{
    "code": 200,
    "msg": "成功",
    "data": "密码修改成功"
}

4. 获取当前用户信息

接口地址: GET /api/auth/current-user
是否需要Token:

请求示例:

// 获取存储的Token
const authToken = localStorage.getItem('token');

// 构建请求头
const requestHeaders = {
    'Authorization': `Bearer ${authToken}`
};

const response = await fetch('http://localhost:8080/api/auth/current-user', {
    method: 'GET',
    headers: requestHeaders
});

const result = await response.json();

响应示例:

{
    "code": 200,
    "msg": "成功",
    "data": "当前登录用户: zhangsan"
}

🔧 前端Token管理

Token存储和使用

class AuthManager {
    // 保存Token - 确保完整保存
    static saveToken(tokenString, userInfo) {
        if (tokenString && tokenString.trim()) {
            localStorage.setItem('token', tokenString);
            localStorage.setItem('userInfo', JSON.stringify(userInfo));
        }
    }

    // 获取完整Token
    static getToken() {
        const tokenValue = localStorage.getItem('token');
        return tokenValue ? tokenValue.trim() : null;
    }

    // 获取用户信息
    static getUserInfo() {
        const userInfo = localStorage.getItem('userInfo');
        return userInfo ? JSON.parse(userInfo) : null;
    }

    // 清除Token登出
    static logout() {
        localStorage.removeItem('token');
        localStorage.removeItem('userInfo');
    }

    // 检查是否已登录
    static isLoggedIn() {
        const token = this.getToken();
        return token && token.length > 0;
    }

    // 创建认证头 - 确保完整Token传递
    static getAuthHeaders() {
        const fullToken = this.getToken();
        return fullToken ? { 'Authorization': `Bearer ${fullToken}` } : {};
    }
}

统一请求封装

class ApiClient {
    static baseURL = 'http://localhost:8080';

    // 通用请求方法 - 使用变量方式确保Token完整传递
    static async request(url, options = {}) {
        // 获取完整Token
        const currentToken = AuthManager.getToken();
        
        // 构建基础headers
        const baseHeaders = {
            'Content-Type': 'application/json'
        };

        // 如果有Token添加认证header
        const authHeaders = currentToken ? { 'Authorization': `Bearer ${currentToken}` } : {};
        
        // 合并headers
        const finalHeaders = {
            ...baseHeaders,
            ...authHeaders,
            ...(options.headers || {})
        };

        const config = {
            ...options,
            headers: finalHeaders
        };

        try {
            const response = await fetch(`${this.baseURL}${url}`, config);
            const result = await response.json();

            // 处理认证失败
            if (response.status === 403 || response.status === 401) {
                AuthManager.logout();
                // 跳转到登录页面
                window.location.href = '/login';
                return null;
            }

            return result;
        } catch (error) {
            console.error('API请求失败:', error);
            throw error;
        }
    }

    // GET请求
    static async get(url) {
        return this.request(url, { method: 'GET' });
    }

    // POST请求
    static async post(url, data) {
        return this.request(url, {
            method: 'POST',
            body: JSON.stringify(data)
        });
    }

    // PUT请求
    static async put(url, data) {
        return this.request(url, {
            method: 'PUT',
            body: JSON.stringify(data)
        });
    }

    // DELETE请求
    static async delete(url) {
        return this.request(url, { method: 'DELETE' });
    }
}

认证相关操作

class AuthAPI {
    // 用户注册
    static async register(userData) {
        return ApiClient.post('/api/auth/register', userData);
    }

    // 用户登录 - 确保完整Token保存
    static async login(credentials) {
        const result = await ApiClient.post('/api/auth/login', credentials);
        
        if (result && result.code === 200) {
            // 获取完整Token并保存
            const completeToken = result.data.token;
            AuthManager.saveToken(completeToken, result.data.accountInfo);
        }
        
        return result;
    }

    // 修改密码 - 使用变量方式传递Token
    static async changePassword(passwordData) {
        // 获取完整Token
        const userToken = AuthManager.getToken();
        
        // 构建请求头
        const requestHeaders = {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${userToken}`
        };

        // 直接使用fetch确保Token完整传递
        const response = await fetch(`${ApiClient.baseURL}/api/auth/change-password`, {
            method: 'PUT',
            headers: requestHeaders,
            body: JSON.stringify(passwordData)
        });

        return await response.json();
    }

    // 获取当前用户 - 使用变量方式传递Token
    static async getCurrentUser() {
        const accessToken = AuthManager.getToken();
        const headers = { 'Authorization': `Bearer ${accessToken}` };
        
        const response = await fetch(`${ApiClient.baseURL}/api/auth/current-user`, {
            method: 'GET',
            headers: headers
        });

        return await response.json();
    }

    // 登出
    static logout() {
        AuthManager.logout();
        window.location.href = '/login';
    }
}

🎯 使用示例

登录页面

// 登录表单提交
async function handleLogin(event) {
    event.preventDefault();
    
    const formData = new FormData(event.target);
    const loginCredentials = {
        accountName: formData.get('username'),
        accountPassword: formData.get('password')
    };

    try {
        const loginResult = await AuthAPI.login(loginCredentials);
        
        if (loginResult.code === 200) {
            alert('登录成功!');
            // 验证Token是否正确保存
            const savedToken = AuthManager.getToken();
            console.log('Token已保存:', savedToken ? '是' : '否');
            
            window.location.href = '/dashboard';
        } else {
            alert(`登录失败:${loginResult.msg}`);
        }
    } catch (error) {
        alert('登录请求失败,请检查网络连接');
    }
}

注册页面

// 注册表单提交
async function handleRegister(event) {
    event.preventDefault();
    
    const formData = new FormData(event.target);
    const registrationData = {
        accountName: formData.get('username'),
        accountPassword: formData.get('password'),
        confirmPassword: formData.get('confirmPassword'),
        accountPhone: formData.get('phone'),
        accountType: formData.get('userType'),
        accountGender: formData.get('gender')
    };

    try {
        const registerResult = await AuthAPI.register(registrationData);
        
        if (registerResult.code === 200) {
            alert('注册成功!请登录');
            window.location.href = '/login';
        } else {
            alert(`注册失败:${registerResult.msg}`);
        }
    } catch (error) {
        alert('注册请求失败,请检查网络连接');
    }
}

修改密码页面

// 修改密码表单提交
async function handleChangePassword(event) {
    event.preventDefault();
    
    const formData = new FormData(event.target);
    const changePasswordInfo = {
        oldPassword: formData.get('oldPassword'),
        newPassword: formData.get('newPassword'),
        confirmPassword: formData.get('confirmPassword')
    };

    try {
        // 使用专门的方法确保Token完整传递
        const changeResult = await AuthAPI.changePassword(changePasswordInfo);
        
        if (changeResult.code === 200) {
            alert('密码修改成功!');
        } else {
            alert(`密码修改失败:${changeResult.msg}`);
        }
    } catch (error) {
        alert('请求失败,请检查网络连接');
    }
}

业务API调用示例

// 获取数据列表 - 使用变量方式确保Token传递
async function fetchDataWithAuth(apiEndpoint) {
    try {
        // 获取完整Token
        const authenticationToken = AuthManager.getToken();
        
        if (!authenticationToken) {
            alert('请先登录');
            window.location.href = '/login';
            return;
        }

        // 构建请求头
        const apiHeaders = {
            'Authorization': `Bearer ${authenticationToken}`
        };

        const response = await fetch(`http://localhost:8080${apiEndpoint}`, {
            method: 'GET',
            headers: apiHeaders
        });

        const result = await response.json();
        
        if (result && result.code === 200) {
            return result.data;
        } else {
            alert(`请求失败:${result.msg}`);
            return null;
        }
    } catch (error) {
        alert('请求失败,请检查网络连接');
        return null;
    }
}

// 发送数据 - 使用变量方式确保Token传递
async function postDataWithAuth(apiEndpoint, postData) {
    try {
        // 获取完整Token
        const userAuthToken = AuthManager.getToken();
        
        if (!userAuthToken) {
            alert('请先登录');
            window.location.href = '/login';
            return;
        }

        // 构建请求头
        const requestHeaders = {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${userAuthToken}`
        };

        const response = await fetch(`http://localhost:8080${apiEndpoint}`, {
            method: 'POST',
            headers: requestHeaders,
            body: JSON.stringify(postData)
        });

        const result = await response.json();
        
        if (result && result.code === 200) {
            return result.data;
        } else {
            alert(`操作失败:${result.msg}`);
            return null;
        }
    } catch (error) {
        alert('请求失败,请检查网络连接');
        return null;
    }
}

🚨 错误处理

常见错误码

错误码 说明 处理方式
200 成功 正常处理数据
401 未认证 跳转到登录页面
403 已禁止/Token无效 清除Token跳转到登录页面
500 服务器错误 显示错误信息,建议重试

Token验证和错误处理

// 页面加载时验证Token有效性
async function validateTokenOnPageLoad() {
    const currentToken = AuthManager.getToken();
    
    if (!currentToken) {
        // 没有Token跳转到登录页
        window.location.href = '/login';
        return;
    }

    try {
        // 测试Token是否有效
        const testHeaders = { 'Authorization': `Bearer ${currentToken}` };
        
        const testResponse = await fetch('http://localhost:8080/api/auth/test', {
            method: 'GET',
            headers: testHeaders
        });

        if (testResponse.status === 403 || testResponse.status === 401) {
            // Token无效清除并跳转
            AuthManager.logout();
            window.location.href = '/login';
        }
    } catch (error) {
        console.error('Token验证失败:', error);
    }
}

// 页面加载时执行Token验证
window.addEventListener('DOMContentLoaded', validateTokenOnPageLoad);

📝 重要注意事项

  1. Token完整性:

    • 始终使用变量方式传递Token避免字符串截断
    • 在存储和读取Token时检查完整性
  2. Token安全:

    • Token存储在localStorage中注意防止XSS攻击
    • 生产环境建议考虑更安全的存储方式
  3. 请求头构建:

    • 使用变量方式构建Authorization头
    • 确保Token前缀"Bearer "正确添加
  4. 错误处理:

    • 统一处理401/403错误自动跳转登录页
    • 为用户提供友好的错误提示
  5. 调试建议:

    • 在控制台检查Token是否完整
    • 验证请求头是否正确设置

这份文档强调了使用变量方式来确保Token完整传递避免了直接字符串拼接可能导致的截断问题