认证接口
用户认证相关的 API 接口,包括登录、注册、退出、Token 刷新等。
用户登录
接口信息
POST /api/auth/login
Content-Type: application/json
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| username | string | 是 | 用户名或邮箱 |
| password | string | 是 | 密码 |
| captcha | string | 否 | 验证码(启用时必填) |
| remember | boolean | 否 | 是否记住登录 |
请求示例
{
"username": "admin",
"password": "admin123",
"captcha": "ABCD",
"remember": true
}
响应参数
| 参数 | 类型 | 说明 |
|---|---|---|
| code | integer | 状态码 |
| message | string | 提示信息 |
| data.token | string | JWT Token |
| data.expire | integer | 过期时间(秒) |
| data.user | object | 用户信息 |
响应示例
成功响应(200)
{
"code": 200,
"message": "登录成功",
"data": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJjbXNfc3lzdGVtIiwiYXVkIjoiY21zX3VzZXIiLCJpYXQiOjE3MDUzMTcwMDAsImV4cCI6MTcwNTMyNDIwMCwiZGF0YSI6eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInJvbGVfaWQiOjF9fQ.xxxxx",
"expire": 7200,
"user": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"nickname": "管理员",
"avatar": "https://example.com/avatar/admin.jpg",
"role": {
"id": 1,
"name": "超级管理员"
},
"permissions": [
"article.view",
"article.create",
"article.update",
"article.delete"
]
}
},
"timestamp": 1705317000
}
失败响应(401)
{
"code": 401,
"message": "用户名或密码错误",
"timestamp": 1705317000
}
验证码错误(400)
{
"code": 400,
"message": "验证码错误",
"timestamp": 1705317000
}
账号被禁用(403)
{
"code": 403,
"message": "账号已被禁用",
"timestamp": 1705317000
}
代码示例
JavaScript (Axios)
import axios from 'axios'
const login = async (username, password) => {
try {
const response = await axios.post('/api/auth/login', {
username,
password,
remember: true
})
if (response.data.code === 200) {
// 保存 Token
localStorage.setItem('token', response.data.data.token)
localStorage.setItem('user', JSON.stringify(response.data.data.user))
return response.data
}
} catch (error) {
console.error('登录失败:', error)
throw error
}
}
// 使用
login('admin', 'admin123')
.then(data => {
console.log('登录成功:', data)
})
.catch(error => {
console.error('登录失败:', error)
})
PHP (cURL)
<?php
$url = 'http://your-domain.com/api/auth/login';
$data = [
'username' => 'admin',
'password' => 'admin123',
'remember' => true,
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
if ($result['code'] === 200) {
$token = $result['data']['token'];
echo "登录成功,Token: $token\n";
} else {
echo "登录失败: " . $result['message'] . "\n";
}
用户注册
接口信息
POST /api/auth/register
Content-Type: application/json
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| username | string | 是 | 用户名(3-20字符) |
| password | string | 是 | 密码(6-20字符) |
| password_confirm | string | 是 | 确认密码 |
| string | 是 | 邮箱地址 | |
| nickname | string | 否 | 昵称 |
| captcha | string | 是 | 验证码 |
| agree | boolean | 是 | 同意服务条款 |
请求示例
{
"username": "newuser",
"password": "password123",
"password_confirm": "password123",
"email": "newuser@example.com",
"nickname": "新用户",
"captcha": "ABCD",
"agree": true
}
响应示例
成功响应(201)
{
"code": 201,
"message": "注册成功",
"data": {
"id": 10,
"username": "newuser",
"email": "newuser@example.com",
"nickname": "新用户"
},
"timestamp": 1705317000
}
用户名已存在(400)
{
"code": 400,
"message": "用户名已被使用",
"timestamp": 1705317000
}
邮箱已注册(400)
{
"code": 400,
"message": "邮箱已被注册",
"timestamp": 1705317000
}
验证失败(422)
{
"code": 422,
"message": "数据验证失败",
"errors": {
"username": ["用户名长度必须在3-20个字符之间"],
"password": ["两次密码输入不一致"],
"email": ["邮箱格式不正确"]
},
"timestamp": 1705317000
}
用户退出
接口信息
POST /api/auth/logout
Authorization: Bearer {token}
请求参数
无
响应示例
成功响应(200)
{
"code": 200,
"message": "退出成功",
"timestamp": 1705317000
}
代码示例
const logout = async () => {
try {
const token = localStorage.getItem('token')
await axios.post('/api/auth/logout', {}, {
headers: {
'Authorization': `Bearer ${token}`
}
})
// 清除本地数据
localStorage.removeItem('token')
localStorage.removeItem('user')
// 跳转到登录页
window.location.href = '/login'
} catch (error) {
console.error('退出失败:', error)
}
}
Token 刷新
接口信息
POST /api/auth/refresh
Authorization: Bearer {token}
请求参数
无
响应示例
成功响应(200)
{
"code": 200,
"message": "Token刷新成功",
"data": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.xxxxx",
"expire": 7200
},
"timestamp": 1705317000
}
Token 已过期(401)
{
"code": 10002,
"message": "Token已过期,请重新登录",
"timestamp": 1705317000
}
代码示例
// 自动刷新 Token
const refreshToken = async () => {
try {
const token = localStorage.getItem('token')
const response = await axios.post('/api/auth/refresh', {}, {
headers: {
'Authorization': `Bearer ${token}`
}
})
if (response.data.code === 200) {
// 更新 Token
localStorage.setItem('token', response.data.data.token)
return response.data.data.token
}
} catch (error) {
// Token 刷新失败,跳转到登录页
window.location.href = '/login'
}
}
// Axios 响应拦截器
axios.interceptors.response.use(
response => response,
async error => {
if (error.response?.status === 401) {
// Token 过期,尝试刷新
const newToken = await refreshToken()
if (newToken) {
// 重试原请求
error.config.headers.Authorization = `Bearer ${newToken}`
return axios(error.config)
}
}
return Promise.reject(error)
}
)
获取当前用户信息
接口信息
GET /api/auth/user
Authorization: Bearer {token}
请求参数
无
响应示例
成功响应(200)
{
"code": 200,
"message": "获取成功",
"data": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"nickname": "管理员",
"avatar": "https://example.com/avatar/admin.jpg",
"phone": "13800138000",
"status": 1,
"role": {
"id": 1,
"name": "超级管理员"
},
"permissions": [
"article.*",
"category.*",
"system.*"
],
"create_time": "2023-01-01 00:00:00",
"last_login_time": "2024-01-15 14:30:25",
"last_login_ip": "192.168.1.100"
},
"timestamp": 1705317000
}
修改密码
接口信息
POST /api/auth/change-password
Authorization: Bearer {token}
Content-Type: application/json
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| old_password | string | 是 | 原密码 |
| new_password | string | 是 | 新密码 |
| new_password_confirm | string | 是 | 确认新密码 |
请求示例
{
"old_password": "old123456",
"new_password": "new123456",
"new_password_confirm": "new123456"
}
响应示例
成功响应(200)
{
"code": 200,
"message": "密码修改成功,请重新登录",
"timestamp": 1705317000
}
原密码错误(400)
{
"code": 400,
"message": "原密码不正确",
"timestamp": 1705317000
}
忘记密码
发送重置邮件
接口信息
POST /api/auth/forgot-password
Content-Type: application/json
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 注册邮箱 | |
| captcha | string | 是 | 验证码 |
请求示例
{
"email": "admin@example.com",
"captcha": "ABCD"
}
响应示例
{
"code": 200,
"message": "重置邮件已发送,请查收",
"timestamp": 1705317000
}
重置密码
接口信息
POST /api/auth/reset-password
Content-Type: application/json
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| token | string | 是 | 邮件中的重置Token |
| password | string | 是 | 新密码 |
| password_confirm | string | 是 | 确认密码 |
请求示例
{
"token": "abc123def456",
"password": "newpassword123",
"password_confirm": "newpassword123"
}
响应示例
{
"code": 200,
"message": "密码重置成功,请使用新密码登录",
"timestamp": 1705317000
}
验证 Token
接口信息
POST /api/auth/verify
Authorization: Bearer {token}
请求参数
无
响应示例
Token 有效(200)
{
"code": 200,
"message": "Token有效",
"data": {
"valid": true,
"expire_at": "2024-01-15 16:30:25"
},
"timestamp": 1705317000
}
Token 无效(401)
{
"code": 10001,
"message": "Token无效",
"data": {
"valid": false
},
"timestamp": 1705317000
}
获取验证码
图形验证码
接口信息
GET /api/captcha
响应示例
{
"code": 200,
"data": {
"key": "captcha_abc123",
"image": "data:image/png;base64,iVBORw0KGgoAAAANS..."
},
"timestamp": 1705317000
}
短信验证码
接口信息
POST /api/sms/send
Content-Type: application/json
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| phone | string | 是 | 手机号码 |
| type | string | 是 | 类型(register/login/reset) |
| captcha | string | 是 | 图形验证码 |
请求示例
{
"phone": "13800138000",
"type": "register",
"captcha": "ABCD"
}
响应示例
{
"code": 200,
"message": "短信验证码已发送",
"data": {
"expire": 300
},
"timestamp": 1705317000
}
错误码
| 错误码 | 说明 |
|---|---|
| 401 | 未认证或认证失败 |
| 403 | 账号被禁用 |
| 10001 | Token 无效 |
| 10002 | Token 已过期 |
| 10003 | 用户不存在 |
| 10004 | 密码错误 |
| 10005 | 验证码错误 |
| 10006 | 账号已被锁定 |
安全建议
Token 存储
推荐方式
// 存储在 localStorage(推荐用于 SPA)
localStorage.setItem('token', token)
// 存储在 Cookie(推荐用于传统网站)
document.cookie = `token=${token}; path=/; secure; httpOnly`
不推荐
// ✗ 不要存储在全局变量
window.token = token
// ✗ 不要存储在 sessionStorage(刷新会丢失)
sessionStorage.setItem('token', token)
HTTPS
生产环境必须使用 HTTPS:
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 其他配置...
}
Token 过期处理
// 请求拦截器:添加 Token
axios.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// 响应拦截器:处理过期
axios.interceptors.response.use(
response => response,
async error => {
const originalRequest = error.config
// Token 过期
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true
try {
// 刷新 Token
const newToken = await refreshToken()
originalRequest.headers.Authorization = `Bearer ${newToken}`
return axios(originalRequest)
} catch (refreshError) {
// 刷新失败,跳转登录
window.location.href = '/login'
return Promise.reject(refreshError)
}
}
return Promise.reject(error)
}
)
防止暴力破解
后端限流
// 登录失败次数限制
$attempts = Cache::get('login_attempts_' . $username, 0);
if ($attempts >= 5) {
return json([
'code' => 429,
'message' => '登录失败次数过多,请30分钟后重试'
]);
}
// 验证密码
if (!$this->checkPassword($password)) {
Cache::set('login_attempts_' . $username, $attempts + 1, 1800);
return json([
'code' => 401,
'message' => '用户名或密码错误'
]);
}
前端延迟
let loginAttempts = 0
const login = async (username, password) => {
// 失败后延迟重试
if (loginAttempts > 0) {
await new Promise(resolve => {
setTimeout(resolve, loginAttempts * 1000)
})
}
try {
const result = await api.login(username, password)
loginAttempts = 0 // 重置
return result
} catch (error) {
loginAttempts++
throw error
}
}
