Files
ShaFaFanXin/管理后台/登录逻辑说明.md

140 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 登录接口逻辑说明
## 问题描述
原先的实现中,登录失败时后端会抛出 `UnauthorizedException`HTTP 401前端响应拦截器收到 401 后会自动清空 token 并跳转到登录页,这在登录页面本身会造成逻辑混乱。
## 解决方案
### 1. 后端修改 (`后端/src/auth/auth.controller.ts`)
**修改前**
```typescript
async login(@Body() loginDto: LoginDto): Promise<any> {
const result = await this.authService.login(loginDto);
return { code: 0, message: 'success', data: result };
}
```
- 如果 auth.service 抛出 `UnauthorizedException`,会直接返回 HTTP 401 状态码
**修改后**
```typescript
async login(@Body() loginDto: LoginDto): Promise<any> {
try {
const result = await this.authService.login(loginDto);
return {
code: 0,
message: '登录成功',
data: result,
};
} catch (error) {
// 登录失败返回统一格式,不抛出 HTTP 异常
return {
code: 401,
message: error.message || '用户名或密码错误',
data: null,
};
}
}
```
**关键变化**
- ✅ HTTP 状态码始终为 200
- ✅ 通过响应体的 `code` 字段区分成功0和失败401
- ✅ 错误信息通过 `message` 字段返回
### 2. 前端修改 (`管理后台/src/utils/request.ts`)
**修改前**
```typescript
if (res.code !== 0 && res.code !== 200) {
ElMessage.error(res.message || '请求失败')
if (res.code === 401) {
const userStore = useUserStore()
userStore.logout()
router.push('/login')
}
return Promise.reject(new Error(res.message || '请求失败'))
}
```
- 所有 401 都会跳转登录页
**修改后**
```typescript
if (res.code !== 0 && res.code !== 200) {
const isLoginRequest = response.config.url?.includes('/auth/login')
if (res.code === 401 && !isLoginRequest) {
ElMessage.error('登录已过期,请重新登录')
const userStore = useUserStore()
userStore.logout()
router.push('/login')
} else if (!isLoginRequest) {
ElMessage.error(res.message || '请求失败')
}
return Promise.reject(new Error(res.message || '请求失败'))
}
```
**关键变化**
- ✅ 区分登录请求和其他请求
- ✅ 登录接口的 401 不会触发自动跳转
- ✅ 登录接口的错误消息不在拦截器中显示(由登录页面处理)
### 3. 前端登录页面 (`管理后台/src/views/login/index.vue`)
保持原有的错误处理逻辑:
```typescript
try {
await userStore.login(loginForm.username, loginForm.password)
ElMessage.success('登录成功')
router.push('/')
} catch (error: any) {
const errorMessage = error.message || '登录失败,请检查用户名和密码'
ElMessage.error(errorMessage)
}
```
**关键点**
- ✅ 登录页面自己处理并显示错误信息
- ✅ 错误消息来自后端返回的 `message` 字段
## 最终效果
### 登录成功
```
请求: POST /api/auth/login
响应: { code: 0, message: '登录成功', data: { user: {...}, access_token: '...', refresh_token: '...' } }
结果: 显示"登录成功",跳转到首页
```
### 登录失败(用户名或密码错误)
```
请求: POST /api/auth/login
响应: { code: 401, message: '用户名或密码错误', data: null }
结果: 显示"用户名或密码错误",停留在登录页
```
### 登录失败(账户被禁用)
```
请求: POST /api/auth/login
响应: { code: 401, message: '账户已被禁用,请联系管理员', data: null }
结果: 显示"账户已被禁用,请联系管理员",停留在登录页
```
### 其他接口 Token 过期
```
请求: GET /api/case/list
响应: { code: 401, message: 'Unauthorized', data: null }
结果: 显示"登录已过期,请重新登录",清空 token跳转到登录页
```
## 总结
这个修改确保了:
1. **登录接口**:失败时返回友好的错误信息,不会触发自动跳转
2. **其他接口**Token 过期时自动清理状态并跳转到登录页
3. **用户体验**:错误提示清晰准确,不会出现重复提示或循环跳转