forked from qq1244/vue-beta
- 新增登录、注册、修改密码页面,支持三种用户类型 - 实现路由守卫和权限控制系统 - 集成Axios拦截器处理认证和错误 - 添加统一错误页面处理 - 完善Header组件退出登录功能,清除本地数据并跳转登录页 - 使用Element Plus构建现代化UI界面 - 支持响应式设计和移动端适配
645 lines
16 KiB
Markdown
645 lines
16 KiB
Markdown
# 餐厅管理系统 - 前端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**: ❌ 否
|
||
|
||
**请求参数**:
|
||
```json
|
||
{
|
||
"accountName": "用户名(3-20字符,字母数字下划线)",
|
||
"accountPassword": "密码(8-20字符,至少包含字母和数字)",
|
||
"confirmPassword": "确认密码(需与密码相同)",
|
||
"accountPhone": "手机号(11位数字,可选)",
|
||
"accountType": "账户类型(admin/manager/customer)",
|
||
"accountGender": "性别(male/female,可选)"
|
||
}
|
||
```
|
||
|
||
**请求示例**:
|
||
```javascript
|
||
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();
|
||
```
|
||
|
||
**响应示例**:
|
||
```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**: ❌ 否
|
||
|
||
**请求参数**:
|
||
```json
|
||
{
|
||
"accountName": "用户名",
|
||
"accountPassword": "密码"
|
||
}
|
||
```
|
||
|
||
**请求示例**:
|
||
```javascript
|
||
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));
|
||
}
|
||
```
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"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**: ✅ 是
|
||
|
||
**请求参数**:
|
||
```json
|
||
{
|
||
"oldPassword": "当前密码",
|
||
"newPassword": "新密码(8-20字符,至少包含字母和数字)",
|
||
"confirmPassword": "确认新密码"
|
||
}
|
||
```
|
||
|
||
**请求示例**:
|
||
```javascript
|
||
// 获取存储的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();
|
||
```
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "成功",
|
||
"data": "密码修改成功"
|
||
}
|
||
```
|
||
|
||
### 4. 获取当前用户信息
|
||
|
||
**接口地址**: `GET /api/auth/current-user`
|
||
**是否需要Token**: ✅ 是
|
||
|
||
**请求示例**:
|
||
```javascript
|
||
// 获取存储的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();
|
||
```
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "成功",
|
||
"data": "当前登录用户: zhangsan"
|
||
}
|
||
```
|
||
|
||
## 🔧 前端Token管理
|
||
|
||
### Token存储和使用
|
||
|
||
```javascript
|
||
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}` } : {};
|
||
}
|
||
}
|
||
```
|
||
|
||
### 统一请求封装
|
||
|
||
```javascript
|
||
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' });
|
||
}
|
||
}
|
||
```
|
||
|
||
### 认证相关操作
|
||
|
||
```javascript
|
||
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';
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🎯 使用示例
|
||
|
||
### 登录页面
|
||
|
||
```javascript
|
||
// 登录表单提交
|
||
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('登录请求失败,请检查网络连接');
|
||
}
|
||
}
|
||
```
|
||
|
||
### 注册页面
|
||
|
||
```javascript
|
||
// 注册表单提交
|
||
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('注册请求失败,请检查网络连接');
|
||
}
|
||
}
|
||
```
|
||
|
||
### 修改密码页面
|
||
|
||
```javascript
|
||
// 修改密码表单提交
|
||
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调用示例
|
||
|
||
```javascript
|
||
// 获取数据列表 - 使用变量方式确保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验证和错误处理
|
||
|
||
```javascript
|
||
// 页面加载时验证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完整传递,避免了直接字符串拼接可能导致的截断问题! |