4.0 KiB
4.0 KiB
登录接口逻辑说明
问题描述
原先的实现中,登录失败时后端会抛出 UnauthorizedException(HTTP 401),前端响应拦截器收到 401 后会自动清空 token 并跳转到登录页,这在登录页面本身会造成逻辑混乱。
解决方案
1. 后端修改 (后端/src/auth/auth.controller.ts)
修改前:
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 状态码
修改后:
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)
修改前:
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 都会跳转登录页
修改后:
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)
保持原有的错误处理逻辑:
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,跳转到登录页
总结
这个修改确保了:
- 登录接口:失败时返回友好的错误信息,不会触发自动跳转
- 其他接口:Token 过期时自动清理状态并跳转到登录页
- 用户体验:错误提示清晰准确,不会出现重复提示或循环跳转