308 lines
7.4 KiB
Markdown
308 lines
7.4 KiB
Markdown
# 前端Token API迁移指南
|
||
|
||
## 📋 概述
|
||
|
||
TokenController接口已经从POST请求体传递token的方式优化为GET请求头传递token的标准方式。如果前端需要使用这些接口,请参考以下迁移指南。
|
||
|
||
## ✅ 当前状态
|
||
|
||
经过检查,前端代码中**没有直接使用**以下接口:
|
||
- `/token/user-info`
|
||
- `/token/username`
|
||
- `/token/validate`
|
||
|
||
前端主要使用的是AuthController中的接口:
|
||
- `/auth/user/info` - 获取用户信息
|
||
- `/auth/username` - 获取用户名
|
||
- `/auth/validateToken` - 验证Token
|
||
|
||
因此,**本次优化不需要修改前端代码**。
|
||
|
||
## 📚 接口使用指南(如果将来需要)
|
||
|
||
### 1. 获取用户信息
|
||
|
||
#### 旧方式(已废弃)
|
||
```typescript
|
||
// ❌ 不推荐:POST请求,token在请求体中
|
||
const response = await http.post('/token/user-info', {
|
||
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
|
||
})
|
||
```
|
||
|
||
#### 新方式(推荐)
|
||
```typescript
|
||
// ✅ 推荐:GET请求,token在请求头中(自动添加)
|
||
const response = await http.get('/token/user-info')
|
||
|
||
// 或者手动指定token(特殊场景)
|
||
const response = await http.get('/token/user-info', {
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`
|
||
}
|
||
})
|
||
```
|
||
|
||
### 2. 获取用户名
|
||
|
||
#### 旧方式(已废弃)
|
||
```typescript
|
||
// ❌ 不推荐
|
||
const response = await http.post('/token/username', {
|
||
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
|
||
})
|
||
```
|
||
|
||
#### 新方式(推荐)
|
||
```typescript
|
||
// ✅ 推荐
|
||
const response = await http.get('/token/username')
|
||
```
|
||
|
||
### 3. 验证Token
|
||
|
||
#### 旧方式(已废弃)
|
||
```typescript
|
||
// ❌ 不推荐
|
||
const response = await http.post('/token/validate', {
|
||
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
|
||
})
|
||
```
|
||
|
||
#### 新方式(推荐)
|
||
```typescript
|
||
// ✅ 推荐
|
||
const response = await http.get('/token/validate')
|
||
```
|
||
|
||
## 🔧 HTTP请求工具配置
|
||
|
||
### 确保请求拦截器自动添加Token
|
||
|
||
大多数项目的HTTP工具已经配置了自动添加Authorization请求头的拦截器:
|
||
|
||
```typescript
|
||
// utils/request.ts 或 utils/http.ts
|
||
import axios from 'axios'
|
||
|
||
const http = axios.create({
|
||
baseURL: import.meta.env.VITE_API_BASE_URL,
|
||
timeout: 10000
|
||
})
|
||
|
||
// 请求拦截器:自动添加Token
|
||
http.interceptors.request.use(
|
||
(config) => {
|
||
// 从localStorage获取token
|
||
const token = localStorage.getItem('access_token')
|
||
|
||
// 如果token存在,添加到请求头
|
||
if (token) {
|
||
config.headers.Authorization = `Bearer ${token}`
|
||
}
|
||
|
||
return config
|
||
},
|
||
(error) => {
|
||
return Promise.reject(error)
|
||
}
|
||
)
|
||
|
||
export { http }
|
||
```
|
||
|
||
### 如果项目还没有配置,请添加以下代码
|
||
|
||
```typescript
|
||
// 1. 在请求拦截器中添加token
|
||
http.interceptors.request.use((config) => {
|
||
const token = localStorage.getItem('access_token')
|
||
if (token && !config.headers.Authorization) {
|
||
config.headers.Authorization = `Bearer ${token}`
|
||
}
|
||
return config
|
||
})
|
||
|
||
// 2. 在响应拦截器中处理401错误
|
||
http.interceptors.response.use(
|
||
(response) => response,
|
||
async (error) => {
|
||
if (error.response?.status === 401) {
|
||
// Token过期或无效,跳转到登录页
|
||
localStorage.removeItem('access_token')
|
||
window.location.href = '/login'
|
||
}
|
||
return Promise.reject(error)
|
||
}
|
||
)
|
||
```
|
||
|
||
## 📝 API服务封装示例
|
||
|
||
如果需要使用TokenController的接口,可以创建以下服务:
|
||
|
||
```typescript
|
||
// services/token.ts
|
||
import { http } from '@/utils/request'
|
||
import type { UserInfo } from '@/types'
|
||
|
||
export const tokenApi = {
|
||
/**
|
||
* 获取用户信息
|
||
* Token会自动从请求头中获取
|
||
*/
|
||
getUserInfo(): Promise<UserInfo> {
|
||
return http.get('/token/user-info')
|
||
},
|
||
|
||
/**
|
||
* 获取用户名
|
||
* Token会自动从请求头中获取
|
||
*/
|
||
getUsername(): Promise<string> {
|
||
return http.get('/token/username')
|
||
},
|
||
|
||
/**
|
||
* 验证Token
|
||
* Token会自动从请求头中获取
|
||
*/
|
||
validateToken(): Promise<string> {
|
||
return http.get('/token/validate')
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🎯 推荐使用AuthController接口
|
||
|
||
实际上,建议前端继续使用AuthController中的接口,因为它们提供了更完整的功能:
|
||
|
||
```typescript
|
||
// services/auth.ts
|
||
import { http } from '@/utils/request'
|
||
|
||
export const authApi = {
|
||
/**
|
||
* 获取当前用户信息
|
||
* 推荐使用这个接口而不是 /token/user-info
|
||
*/
|
||
getUserInfo(): Promise<UserInfo> {
|
||
return http.get('/auth/user/info')
|
||
},
|
||
|
||
/**
|
||
* 获取用户名
|
||
* 推荐使用这个接口而不是 /token/username
|
||
*/
|
||
getUsername(): Promise<string> {
|
||
return http.get('/auth/username')
|
||
},
|
||
|
||
/**
|
||
* 验证Token
|
||
* 推荐使用这个接口而不是 /token/validate
|
||
*/
|
||
validateToken(): Promise<boolean> {
|
||
return http.get('/auth/validateToken')
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🔍 接口对比
|
||
|
||
| 功能 | TokenController | AuthController | 推荐使用 |
|
||
|------|----------------|----------------|---------|
|
||
| 获取用户信息 | `GET /token/user-info` | `GET /auth/user/info` | AuthController |
|
||
| 获取用户名 | `GET /token/username` | `GET /auth/username` | AuthController |
|
||
| 验证Token | `GET /token/validate` | `GET /auth/validateToken` | AuthController |
|
||
|
||
**推荐理由**:
|
||
- AuthController接口功能更完整
|
||
- 已经在项目中广泛使用
|
||
- 有更好的错误处理和日志记录
|
||
- 与认证流程集成更紧密
|
||
|
||
## ⚠️ 注意事项
|
||
|
||
### 1. Token存储位置
|
||
确保token存储在正确的位置:
|
||
```typescript
|
||
// 登录成功后存储token
|
||
localStorage.setItem('access_token', response.accessToken)
|
||
|
||
// 登出时清除token
|
||
localStorage.removeItem('access_token')
|
||
```
|
||
|
||
### 2. Token格式
|
||
确保token以正确的格式发送:
|
||
```
|
||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||
```
|
||
|
||
### 3. 错误处理
|
||
处理token相关的错误:
|
||
```typescript
|
||
try {
|
||
const userInfo = await tokenApi.getUserInfo()
|
||
} catch (error) {
|
||
if (error.response?.status === 401) {
|
||
// Token无效或过期
|
||
console.error('Token无效,请重新登录')
|
||
// 跳转到登录页
|
||
router.push('/login')
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. Token刷新
|
||
实现token自动刷新机制:
|
||
```typescript
|
||
http.interceptors.response.use(
|
||
(response) => response,
|
||
async (error) => {
|
||
const originalRequest = error.config
|
||
|
||
if (error.response?.status === 401 && !originalRequest._retry) {
|
||
originalRequest._retry = true
|
||
|
||
try {
|
||
// 尝试刷新token
|
||
const refreshToken = localStorage.getItem('refresh_token')
|
||
const response = await authApi.refreshToken({ refreshToken })
|
||
|
||
// 更新token
|
||
localStorage.setItem('access_token', response.accessToken)
|
||
|
||
// 重试原始请求
|
||
originalRequest.headers.Authorization = `Bearer ${response.accessToken}`
|
||
return http(originalRequest)
|
||
} catch (refreshError) {
|
||
// 刷新失败,跳转到登录页
|
||
localStorage.clear()
|
||
window.location.href = '/login'
|
||
return Promise.reject(refreshError)
|
||
}
|
||
}
|
||
|
||
return Promise.reject(error)
|
||
}
|
||
)
|
||
```
|
||
|
||
## ✅ 总结
|
||
|
||
1. **当前状态**:前端代码不需要修改,因为没有使用TokenController接口
|
||
2. **推荐做法**:继续使用AuthController接口
|
||
3. **如果需要使用TokenController**:
|
||
- 使用GET方法
|
||
- Token在请求头中自动传递
|
||
- 确保HTTP工具配置了请求拦截器
|
||
4. **最佳实践**:
|
||
- Token存储在localStorage
|
||
- 使用Bearer格式
|
||
- 实现自动刷新机制
|
||
- 处理401错误
|
||
|
||
这次优化使后端接口更加标准化和安全,为将来的扩展打下了良好的基础!
|