一、实验题目
仿QQWeb即时聊天系统
二、实验目的
- 熟悉WebSocket协议,掌握实时通信技术
- 掌握前后端分离开发模式,前端使用Vue3,后端使用Spring Boot
- 设计并实现一个简单的实时聊天系统
- 提高数据库设计与管理能力
- 实现基本的用户管理和聊天功能
功能要求
- 实现Web的点对点即时的文本消息聊天功能。
- 实现Web的表情的发送、接收和显示功能。
- 实现Web的图片的发送、接收和显示功能。
- 实现本地消息的存储,在离线的时候也能加载和查看历史消息;
- 要求使用WebSocket;
三、总体设计
背景知识
WebSocket是一种在单个TCP连接上进行全双工通信的协议,广泛应用于需要实时通信的场景。传统的 HTTP 请求-响应模型不同,WebSocket 允许服务器和客户端之间实时、低延迟的双向通信,减少了因频繁创建连接所带来的开销,适合用于聊天系统、实时通知、在线游戏等应用。
系统功能
- 用户注册和登录:用户可以注册账号,并通过登录进入系统
- 好友管理:用户可以添加好友,查看好友列表
- 实时聊天:用户可以与好友进行实时消息发送和接收
- 群聊功能:用户可以创建和加入群聊
- 消息存储:聊天记录持久化,用户可以查看历史聊天记录
数据库设计
数据库采用MySQL,主要表结构设计如下:
- 用户表(users)
- id:用户唯一标识(主键)
- username:用户名
- password:密码(加密存储)
- email:用户邮箱
- 好友关系表(friends)
- user_id:用户ID
- friend_id:好友ID
- status:好友关系状态(如0表示请求中,1表示已添加)
- 消息表(messages)
- id:消息ID(主键)
- sender_id:发送者ID
- receiver_id:接收者ID
- content:消息内容
- timestamp:消息发送时间
- 群组表(groups)
- id:群组ID(主键)
- group_name:群组名称
- 群组成员表(group_members)
- group_id:群组ID
- user_id:用户ID
- 群组消息表(group_messages)
- id:消息ID(主键)
- group_id:群组ID
- sender_id:发送者ID
- content:消息内容
- timestamp:消息发送时间
表之间的关联关系
后端功能模块
用户管理模块
-
- 注册:用户提供用户名、密码、邮箱进行注册
- 登录:用户提供用户名和密码进行登录,校验通过后生成会话
- 用户信息:获取和更新用户信息
好友管理模块
-
- 添加好友:发送好友请求,对方同意后成为好友
- 好友列表:获取当前用户的好友列表
聊天模块
-
- 单聊:用户之间点对点的消息发送与接收
- 群聊:群组内的消息发送与接收
WebSocket管理模块
-
- 连接管理:建立和管理WebSocket连接
- 消息处理:处理消息的发送和接收,包含单聊和群聊
前端设计
前端采用Vue3框架,主要页面包括:
- 登录页面
- 注册页面:根据邮箱发送验证码进行注册
- 主页面:包含好友列表、聊天窗口、群组列表
- 修改头像界面
- 聊天页面:显示当前聊天记录,支持发送消息,发送表情和图片
- 修改密码界面
- 忘记密码界面:根据邮箱发送验证码进行改密界面截图: 的关联关
Tocken的使用
Token是一种用于身份验证和授权的令牌(Token)机制,在网络通信中广泛使用。它是一个字符串,代表着用户的身份或权限,用于验证用户在系统中的访问权限。
在身份验证方面,Token通常用于替代传统的基于会话的身份验证机制,如使用Cookie+Session的方式。使用Token进行身份验证的好处是,服务器不需要在内存中保存用户的会话信息,因为Token本身包含了所有验证所需的信息。这使得Token在分布式系统或无状态的API接口中非常适用。Token通常由服务器生成,并在用户登录或进行身份验证时发放给客户端。客户端将Token存储起来,并在后续的请求中将Token作为身份认证的凭证发送给服务器。服务器接收到Token后,可以通过验证Token的有效性来确认用户的身份和权限。常见的Token类型包括JWT(JSON Web Token)、OAuth 2.0的访问令牌(Access Token)、Bearer Token等。
在前端,使用浏览器提供的 Web Storage(如LocalStorage或SessionStorage)或者使用HTTP Cookie来存储Token。
使用LocalStorage:
// 存储Token到LocalStorage
localStorage.setItem('token', 'your_token_value');
// 从LocalStorage读取Token
const token = localStorage.getItem('token');
使用SessionStorage:
// 存储Token到SessionStorage
sessionStorage.setItem('token', 'your_token_value');
// 从SessionStorage读取Token
const token = sessionStorage.getItem('token');
使用HTTP Cookie:
// 存储Token到Cookie
document.cookie = 'token=your_token_value; expires=...; path=/';
// 从Cookie读取Token
const cookies = document.cookie.split(';');
let token = null;
cookies.forEach(cookie => {
const [name, value] = cookie.trim().split('=');
if (name === 'token') {
token = value;
}
});
在前端进行Token验证时,通过在请求头中添加Authorization字段,并将Token值作为其值传递给后端。在后端的代码中,使用@RequestHeader("Authorization") String token来获取请求头中的Token值进行验证和处理。
// axios请求拦截器
httpInstance.interceptors.request.use(
(config) => {
// 1. 从pinia获取token数据
const userStore = useUserStore();
let token = "";
// console.log("userStore.userInfo.data", userStore.userInfo.value.data.token);
// 2. 按照后端的要求拼接token数据
if (userStore.userInfo.value&&userStore.userInfo.value