forked from qq1244/vue-beta
- 新增登录、注册、修改密码页面,支持三种用户类型 - 实现路由守卫和权限控制系统 - 集成Axios拦截器处理认证和错误 - 添加统一错误页面处理 - 完善Header组件退出登录功能,清除本地数据并跳转登录页 - 使用Element Plus构建现代化UI界面 - 支持响应式设计和移动端适配
16 KiB
16 KiB
餐厅管理系统 - 前端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);
📝 重要注意事项
-
Token完整性:
- 始终使用变量方式传递Token,避免字符串截断
- 在存储和读取Token时检查完整性
-
Token安全:
- Token存储在localStorage中,注意防止XSS攻击
- 生产环境建议考虑更安全的存储方式
-
请求头构建:
- 使用变量方式构建Authorization头
- 确保Token前缀"Bearer "正确添加
-
错误处理:
- 统一处理401/403错误,自动跳转登录页
- 为用户提供友好的错误提示
-
调试建议:
- 在控制台检查Token是否完整
- 验证请求头是否正确设置
这份文档强调了使用变量方式来确保Token完整传递,避免了直接字符串拼接可能导致的截断问题!