项目介绍
项目已开源gitee:
技术选型
后端:SpringBoot(WEB)+ JWT + MyBatis-plus +MySql5.7 + Redis + SpringFileStroage
前端:Vue2.0 + ElementUI
技术难点
- 多端聊天(长链接的实现)- WebSocket
- 文件存储、消息的存储 - IO\Reids\Mysql
- 登录权限的验证(完整版登录功能)- JWT\行为验证码\拦截器、过滤器(SpringBoot)
后端框架的搭建
1. SpringBoot框架构建
1.下载工具
使用idea工具(企业版)
下载地址:https://www.jetbrains.com/idea/download/#section=windows
找破解工具破解
2.配置maven工具
下载地址:https://maven.apache.org/download.cgi
1.下载解压好配置环境变量:
配置mavne-》bin目录添加到path环境变量中
2. 修改maven的配置:
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<!-- jar包下载的位置 -->
<localRepository>F:\GjbSVN\maven仓库</localRepository>
<pluginGroups>
</pluginGroups>
<proxies>
</proxies>
<servers>
</servers>
<mirrors>
<!-- 修改mavn仓库的镜像资源位置 -->
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*,!jeecg,!jeecg-snapshots,!getui-nexus</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>
<profiles>
</profiles>
</settings>
3.idea构建springBoot项目
此时会打开项目窗口,然后要进行idea的maven配置修改
完成后项目会从新下载相关的jar包,稍等片刻
接下来启动程序:
2. 网络接口的编写测试
controller类编写
/**
* 模块名称:测试控制器类
* 模块类型:Controller
* 编码人:高靖博
* 创建时间:2023/4/13
* 联系电话:18587388612
*/
// 协议名称:// 服务器ip地址:服务端口号/资源地址
// http://localhost:9010/test/test1
@RestController
@RequestMapping("/test")
public class TestController {
// 只允许GET类型请求
// 查询:GET 提交或加密: POST 修改:PUT 删除:DELETE
@GetMapping("/test1")
public String test1(){
return "<h1>只因你太美!</h1>";
}
// 返回值类型只要不是String,都会将对象转换为json数据返回到前端
@GetMapping("/test2")
public MyResult test2(){
return new MyResult();
}
}
3.SpringBoot集成MyBatis-plus
1.打开pom.xml引入依赖
<!-- 新:版本升级 mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!-- freemarker-模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- mp代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!-- 阿里fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.80</version>
</dependency>
2. 配置mybatis-plus
在resources目录下,将application.properties文件删除改为application.yml文件
# 修改springBoot端口号
server:
port: 9010
# mybatis-plus相关内容
mybatis-plus:
#dto别名映射 !!!!!!!需要修改
type-aliases-package: com.webchartserver
#xml文件路径映射(xml文件要和接口文件同名)!!!!!!!需要修改
mapper-locations: com/webchartserver/**/dao/mapper/**.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启日志(需要引入log4j日志框架)
map-underscore-to-camel-case: false
# 全局变量配置
# 逻辑删除-如:每个表中都有一个通用的字段isDelete描述当前数据是否被删除,1:已删除 0:未删除
global-config:
db-config:
# 当逻辑删除应该设置什么值:1
logic-delete-value: 1
logic-not-delete-value: 0
logic-delete-field: isDelete # 所有表中都要有一个isDelete字段且字段类型是int类型
3.编写一个代码生成工具
package com.webchartserver.core.mpganer;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
/**
* 模块名称:
* 模块类型:
* 编码人:高靖博
* 创建时间:2023/4/13
* 联系电话:18587388612
*/
public class MyBatisGer {
// 数据库的连接 !!!需要修改
private final static String URL = "jdbc:mysql://localhost:3306/webchart?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai";
// 数据库用户名
private final static String USER_NAME = "root";
// 数据库密码
private final static String PWD = "cxk666";
// 每个类作者的名字
private final static String AUTHOR = "GaoJingBo";
// 生成的代码输出的目录
private final static String OUT_PUT_DIR = "D://mybatis";
// 修改根包路径
private final static String PARENT_PACKAGE_NAME = "com.webchartserver";
// 业务模块名
private final static String PARENT_MODEL_NAME = "user";
// 要生成的业务表
private final static String TABLE_NAME = "t_user";
// 如果表名有前缀可以去除前缀
private final static String PREFIX = "t_";
public static void main(String[] args) {
FastAutoGenerator.create(URL, USER_NAME, PWD)
.globalConfig(builder -> {
builder.author(AUTHOR) // 设置作者
.fileOverride() // 覆盖已生成文件
.dateType(DateType.ONLY_DATE)// 日期类型
.commentDate("yyyy-MM-dd") //公共默认日期格式
.outputDir(OUT_PUT_DIR); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent(PARENT_PACKAGE_NAME) //设置父路径根包
.moduleName(PARENT_MODEL_NAME) //设置模块名称
.entity("dto") //dto实体
.service("service") //业务接口
.serviceImpl("service.impl") //业务实现
.mapper("mapper") //mybatis-plus-mapper接口
.xml("mapper.xml") mybatis-plus-mapper接口映射xml
.controller("controller") //控制器
.other("other");
})
.strategyConfig(builder -> {
builder.controllerBuilder()
.enableHyphenStyle()
.enableRestStyle();
builder.mapperBuilder()
.enableMapperAnnotation();
builder.entityBuilder()
.enableLombok()
.logicDeleteColumnName("isDelete")// 逻辑删除表字段名
.logicDeletePropertyName("isDelete")// 逻辑删除实体属性名
// 添加填充规则
.addTableFills(new Column("insertTime", FieldFill.INSERT))
.addTableFills(new Column("updateTime", FieldFill.INSERT_UPDATE))
.idType(IdType.NONE);
builder.addInclude(TABLE_NAME) // 设置需要生成的表名
.enableSkipView()//跳过视图
.addTablePrefix(PREFIX); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
4. 准备一个数据表,进行代码生成
用户信息表
用户信息表-ER图
CREATE TABLE `t_user` (
`userID` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户id',
`insertID` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '记录插入者帐号',
`insertIP` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '记录插入者IP',
`updateTime` datetime(0) NULL DEFAULT NULL COMMENT '记录更新时间',
`updateID` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '记录更新者帐号',
`updateIP` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '记录更新者IP',
`isDelete` int(11) NULL DEFAULT 0 COMMENT '是否删除',
`comment` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注',
`code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '编号',
`realName` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '真实姓名',
`nickName` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '昵称',
`sex` int(11) NULL DEFAULT NULL COMMENT '性别:1:男 2:女 3:保密',
`tellNumber` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手机号码',
PRIMARY KEY (`userID`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
5. SpringBoot集成数据源连接池-阿里巴巴druid
数据库连接池的作用:
- 减少创建连接的io过程
- 使用连接的方式更简单,管理也更加简单
- 满足高并发场景下重复读写创建连接的业务
1.引入依赖
<!-- 数据源依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
2. 在application.yml中配置druid连接池
# spring框架配置
spring:
# 数据源配置
datasource:
# druid连接池
type: com.alibaba.druid.pool.DruidDataSource
# mysql驱动
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据库连接地址 !!!! 需要修改
url: jdbc:mysql://localhost:3306/webchart?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
# 用户名、密码
username: root
password: cxk666
# druid连接池配置
# 初始化连接池最大值,连接中活跃数量最大值
initial-size: 10
max-active: 8
# 获取连接等待的最长时间(毫秒)
max-wait: 60000
# 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
test-while-idle: true
# 既作为检测的间隔时间又作为testWhileIdel执行的依据
time-between-eviction-runs-millis: 60000
# 销毁线程时检测当前连接的最后活动时间和当前时间差大于该值时,关闭当前连接(配置连接在池中的最小生存时间)
min-evictable-idle-time-millis: 30000
# 用来检测数据库连接是否有效的sql 必须是一个查询语句(oracle中为 select 1 from dual)
validation-query: select 1 from dual
# 申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
test-on-borrow: false
# 归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
test-on-return: false
# 是否缓存preparedStatement, 也就是PSCache,PSCache对支持游标的数据库性能提升巨大,比如说oracle,在mysql下建议关闭。
pool-prepared-statements: false
# 置监控统计拦截的filters,去掉后监控界面sql无法统计,stat: 监控统计、Slf4j:日志记录、waLL: 防御sqL注入
filters: stat,wall,slf4j
# 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
max-pool-prepared-statement-per-connection-size: -1
# 合并多个DruidDataSource的监控数据
use-global-data-source-stat: true
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
web-stat-filter:
# 是否启用StatFilter默认值true
enabled: true
# 添加过滤规则
url-pattern: /*
# 忽略过滤的格式
exclusions: /druid/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico
stat-view-servlet:
# 是否启用StatViewServlet默认值true
enabled: true
# 访问路径为/druid时,跳转到StatViewServlet !!!! 需要修改
url-pattern: /druid/*
# 是否能够重置数据
reset-enable: false
# 需要账号密码才能访问控制台,默认为root
login-username: admin
login-password: 123456
# IP白名单
allow: 127.0.0.1
# IP黑名单(共同存在时,deny优先于allow)
deny:
启动项目,测试druid连接池是否正常:
http://localhost:9010/druid/index.html
4.SpringBoot+MP的低代码CRUD
package com.webchartserver.user.controller;
import com.webchartserver.core.dto.MyResult;
import com.webchartserver.user.dto.User;
import com.webchartserver.user.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
*
* 用户信息管理
* @author GaoJingBo
* @since 2023-04-13
*/
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
// 注入业务层实现
@Resource(name = "userServiceImpl")
private IUserService userService;
/**
* 用户添加
* @param user 要添加的用户信息
* @return
*/
@PostMapping("/add")
public MyResult add(@RequestBody User user){
log.debug("-- 添加用户");
MyResult result = new MyResult();
boolean save = userService.save(user);
result.setMsg("用户注册成功!");
return result;
}
/**
* 修改用户
* @param user 要修改的用户信息(前端需要传递要修改的主键字段值)
* @return
*/
@PutMapping ("/update")
public MyResult update(@RequestBody User user){
log.debug("-- 修改用户");
MyResult result = new MyResult();
boolean save = userService.updateById(user);
result.setMsg("用户修改成功!");
return result;
}
/**
* 删除用户
* @param pkID 传递主键字段值
* @return
*/
@DeleteMapping ("/deletes")
public MyResult deletes(String pkID){
log.debug("-- 删除用户");
MyResult result = new MyResult();
boolean b = userService.removeById(pkID);
result.setMsg("删除成功!");
return result;
}
}
5. SpringBoot整合WebSocket
1. 引入webSocket依赖
<!-- 服务端WebSocket需要的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2. 编写一个WebSocket服务配置类
搭建一个webSocket服务端
package com.webchartserver.core.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* 模块名称: 开启WebSocket服务
* 模块类型:
* 编码人:高靖博
* 创建时间:2023/4/14
* 联系电话:18587388612
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
创建一个消息转发的业务类
package com.webchartserver.core.config;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* 模块名称:WebSocket-聊天程序消息转发处理类
* 模块类型:
* 编码人:高靖博
* 创建时间:2023/4/14
* 联系电话:18587388612
*/
@Component
@ServerEndpoint("/wxserver/{wxNumber}")
// ws://loaclhost:9010/wxserver
public class WebSocket {
static{
System.out.println("-------------------------------------------------");
System.out.println("--------------WebSocket服务初始化------------------");
System.out.println("-------------------------------------------------");
}
// 当前客户端的会话对象
private Session session;
// 当前客户端的微信号
private String nowWxNumber;
// 用来存在线连接用户信息( 线程安全 )
// key: 微信号 value: 每个客户端的session对象
private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<String,Session>();
// 保存每一个连接的客户端对象(在线客户端)
private static CopyOnWriteArraySet<WebSocket> webSockets =new CopyOnWriteArraySet<>();
/**
* 当客户端连接时会触发该方法
*/
@OnOpen
public void onOpen(Session session,@PathParam("wxNumber") String wxNumber){
System.err.println("---客户端连接!");
sessionPool.put(wxNumber,session);
this.session = session;
this.nowWxNumber = wxNumber;
webSockets.add(this);
}
/**
* 当客户端发送消息给服务器时触发
* @param msg 客户端发送给服务器的数据内容
*/
@OnMessage
public void onMsg(String msg){
System.err.println("客户端发送消息:"+msg);
// 遍历所有已连接到服务器的集合进行挨个发送信息
for(WebSocket webSocket:webSockets){
// 判断客户端是否断开连接
if(webSocket.session.isOpen()==true){// 还在线
webSocket.session.getAsyncRemote().sendText(msg);
}
}
}
/**
* 客户端断开连接时触发
*/
@OnClose
public void onClose(){
System.err.println("--客户端断开连接");
}
/**
* 当客户端发送消息或服务器端出现异常时触发
* @param e
*/
@OnError
public void onErr(Throwable e){
e.printStackTrace();
System.err.println("--WebSocket异常!!!!");
}
}
前端测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>测试</title>
<style>
/* 历史信息窗口样式 */
.hsDiv{
border:1px solid black;
border-radius: 11px;
width: 100%;
height: 500px;
overflow: auto;
box-sizing: border-box;
padding: 8px;
}
/* 消息的时间文字样式 */
.time{
font-size: 10px;
color:gray;
}
/* 消息的文字内容样式 */
.msgText{
color:blue;
}
/* 发送人信息 */
.person{
font-size: 7px;
color:black;
}
</style>
</head>
<body>
<span id="state">离线</span>
<span id="qqNumber"></span>
<input id="nickName" placeholder="定义一个你的昵称" />
<button onclick="toConect()">连接</button>
<hr/>
<button onclick="sendMsg()">发送</button>
<input id="msg" placeholder="请输入消息...." />
<hr/>
<!-- 历史信息框体 -->
<div class="hsDiv" id="hsDiv">
</div>
</body>
<script>
var qqNumber;
var webScoket;
var htmls = ""; // 所有历史信息html
var nikeName = ""; // 客户端昵称
// 连接到服务器
function toConect(){
// 生成一个唯一的数字
qqNumber = parseInt(Math.random()*10000000000000);
webScoket = new WebSocket("ws://localhost:9010/wxserver/"+qqNumber);
// 定义一个连接成功的回调函数
webScoket.onopen = function (){
console.dir("-连接成功!");
document.getElementById("state").innerHTML = "在线";
document.getElementById("qqNumber").innerHTML = qqNumber;
// 获取连接的昵称信息
var nikeNames = document.getElementById("nickName").value;
nikeName = nikeNames;
}
// 定义一个服务器发送消息给客户端的回调函数
webScoket.onmessage = function(data){
console.dir("-接收到消息:"+data.data);
// QQ信息$消息内容$发送时间$昵称
var msgArr = data.data.split("$");
htmls += '<span class="time">时间:'+msgArr[2]+'</span><br/>'+
'<span class="person">['+msgArr[3]+']</span><br/>'+
'<span class="msgText">'+msgArr[1]+'</span>'+
'<hr/>';
// 动态html代码段添加进入聊天历史信息框中
document.getElementById("hsDiv").innerHTML = htmls;
}
}
// 发送消息方法
function sendMsg(){
// 得到要发送的信息文字
var msg = document.getElementById("msg").value;
// 发送消息的格式:
// QQ信息$消息内容$发送时间$昵称
var nowDate = new Date();
var sendMsgStr = qqNumber+"$"+msg+"$"+(nowDate.getFullYear() +"-"+nowDate.getMonth()+"-"+nowDate.getDate()+" "+nowDate.getHours()+":"+nowDate.getMinutes()+":"+nowDate.getSeconds()+"$"+nikeName)
webScoket.send(sendMsgStr); // 消息发送给服务器了
}
</script>
</html>
前端框架搭建
1. npm、cnpm node环境搭建vue脚手架
1. 安装node.js环境
1.进入官网
https://nodejs.org/en/
2.安装msi,安装目录到C盘
安装好后,使用cmd执行 node- v npm -v看看是否都输出了版本号
3.在除C盘外创建一个nodejsFile文件夹,创建两个文件夹 node_global和node_cache,然后运行以下命令
npm config set prefix "D:\Program Files\nodejs\node_global"
npm config set cache "D:\Program Files\nodejs\node_cache"
4.检查当前系统变量中,path路径有没有叫C:\ProgramFiles\nodejs\node_modules
如果有,创建一个同名路径文件夹在C盘之外的盘符,如果没有就不用管了
- 在path变量中配置你的C:\Program Files\nodejs\node_modules路径
2. 安装淘宝镜像 cnpm
功能:和npm一样,npm是从海外镜像仓库下载,cnmp 是从阿里淘宝镜像仓库下载
npm install -g cnpm --registry=https://registry.npm.taobao.org
安装完成使用:cnpm -v 查看版本
3.安装webpack
npm install webpack -g
安装完成使用:npm webpack -v 查看版本
4. 下载vue脚手架
1.下载脚手架资源
cnpm install -g vue-cli@2.x
2.去到你的gloab目录查看是否有vue开头的文件
- 到别的文件夹下创建一个空的文件夹,在文件夹中按住shift+鼠标右键,点击在此处打开pwershell窗口
- 打开窗口输入:vue init webpack
5.初始化构建vue脚手架
6.运行vue项目
在项目根目录下按住shift+鼠标右键,点击在此处打开pwershell窗口,执行 npm run dev
2. vue项目中安装axios组件(ajax组件)
1.在项目目录下使用cmd执行
//1. 在项目窗口中执行
cnpm install axios
代表安装成功
2.main.js配置导入相关组件
import axios from 'axios'
Vue.prototype.$axios = axios //全局属性
3. 编写一个请求工具类
作用: 封装关于http请求和响应处理的代码
// 1. 导入axios组件
import axios from "axios";
// 导入el组件
import ElementUI from 'element-ui'
// 导入路由组件
import router from '@/router'
const service = axios.create({// get post delete put
baseURL:'/api', // 后端服务地址
timeout:30000 // 请求超时时间
});
// http request 请求拦截器
service.interceptors.request.use(config => {
// 需要所有的请求都带有token
config.headers.token = window.localStorage.getItem("token");
return config;
}, error => {
// 对请求错误做些什么
return Promise.reject(error);
});
// 响应拦截器
service.interceptors.response.use(response => {
// 当后端返回数据或者后端请求错误,我们需要处理相关内容
if(response.status==200){// 请求发送响应成功
if(response.data.code=="-1"){// 认证失败
ElementUI.Notification.error({
title: '系统异常',
message: response.data.msg+",请重新登录"
});
router.push("/");
}else if(response.data.code=="200"){
if(response.data.msg){
ElementUI.Message.success(response.data.msg);
}
}else if(response.data.code=="500"){
ElementUI.Message.error(response.data.msg);
}
return response.data;
}else{
ElementUI.Notification.error({
title: '系统异常',
message: error.response.data.msg+",请联系管理员"
});
return response.data;
}
});
export default service;
4.设置跨域请求问题
在vue中配置一个代理注册工具
3. 使用VSCode构建Vue项目
下载VsCode
https://code.visualstudio.com/
组件下载
创建vue项目
- 创建一个空的项目文件夹
- 使用vsCode打开这个文件夹
- 设置vsCode语言:ctrl+shift+P 输入指令:Display language 先选择英文重启后再选择中文再重启就好了
- 打开终端
vue init webpack
会出现错误:not promite 。。。。 vsCode终端控制台没有系统写入权限
使用本机自带的cmd窗口使用管理员权限启动
将目录跳转至项目路径再执行vue init webpack指令
5.启动项目
npm run dev
6.退出运行
ctrl+c
4. 编写第一个vue文件
<!-- Dom页面 -->
<template>
<!-- 所有的页面元素都要卸载div之内 -->
<div id='UserIndex'>
</div>
</template>
<!-- 组件js -->
<script>
export default {
name:'UserIndex',
data(){
}
}
</script>
<!-- CSS代码 -->
<style scoped>
</style>
使用VSCode创建一个自定义代码片段
文件–》首选项—》配置用户代码片段
{
"Print to console": {
"prefix": "myvue",// 代码段名称
"body": [ //代码内容
"<!-- Dom页面 -->",
"<template>",
"<div id=''>",
"",
"</div>",
"</template>",
"",
"<!-- 组件js -->",
"<script>",
"export default {",
"name:'',",
"data(){",
"",
"}",
"}",
"</script>",
"",
"<!-- CSS代码 -->",
"<style scoped>",
"",
"</style>"
],
"description": "myvue"
}
}
业务实现部分
1.用户注册与登录
1.用户注册功能
后端部分代码
- 后端接收到注册信息,需要将密码进行MD5加密
引入hutool工具包加密方法加密
<!-- hutool工具 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.15</version>
</dependency>
- 注册controller 方法修改
@PostMapping("/add")
public MyResult add(@RequestBody User user){
log.debug("-- 注册用户");
MyResult result = new MyResult();
// 密码加密
String pwd_md5Hex = DigestUtil.md5Hex(user.getPwd());
user.setPwd(pwd_md5Hex);
boolean save = userService.save(user);
result.setMsg("用户注册成功!");
return result;
}
2.用户登录功能
后端代码部分
前端传递了明文的密码需要将明文密码加密后用密文去比对数据库
@PostMapping("/login")
public MyResult login(@RequestBody User user){
log.debug("-- 登录");
MyResult result = new MyResult();
// 1. 查询数据库中和用户名相同的数据
QueryWrapper<User> queryWrapper = new QueryWrapper<>(); // from t_user
// where nickName = ""
queryWrapper.eq("nickName",user.getNickName());
// 2. mp 查询 select * from t_user where nickName = "user.getNickName()"
List<User> list = userService.list(queryWrapper);
if(list.size()>0){ // 用户名存在
User dbUserData = list.get(0);
String pwdMD5 = dbUserData.getPwd();// 取出数据库中保存的密码-密文
String pwd_md5Hex = DigestUtil.md5Hex(user.getPwd()); // 将前端的明文密码加密比较
if(pwd_md5Hex.equals(pwdMD5)){ // 密码输入正确
result.setCode(200);
result.setMsg("登录成功!");
}else{ // 密码输入错误
result.setCode(500);
// 已错误次数累加1
dbUserData.setErrCount(dbUserData.getErrCount()+1);
// 剩余几次可以输入密码的机会 = errCount - 已错误的次数
int lessCount = errCount - dbUserData.getErrCount();
// 判断是否需要锁定
if(lessCount<=0){ //需要锁定
dbUserData.setIsLock(1); // 设置账号锁定
// 返回错误信息给前端
result.setMsg("错误次数过多,账号已锁定!");
}else{
result.setMsg("用户名或密码错误,还剩【"+lessCount+"】次机会!");
}
// 更新数据库
userService.updateById(dbUserData);
}
}else{// 用户名不存在
result.setCode(500); // 业务编码500描述业务不成功
result.setMsg("用户名或密码错误!");
}
2. 实现系统鉴权功能
验证用户是否已经完成了登录,只有用户正常登录之后才能访问系统当中的接口
1.实现方法使用JWT框架
1. 引入jwt依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.0</version>
</dependency>
2. 创建一个JWT工具类
作用:生成token和验证token功能
package com.webchartserver.core.config;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Date;
/**
* 模块名称:JWT权限验证工具包
* 模块类型:
* 编码人:高靖博
* 创建时间:2023/4/14
* 联系电话:18587388612
*/
public class JWTUtil {
private JWTUtil(){}
// 过期时间,单位秒
private static final long EXP_TIME = 60 * 30L;
// 秘钥关键字
private static final String SECRET = "webchart";
/**
* 生成token
* @param userName 登录用户名
* @return
*/
// 生成token时,需要添加token信息
// userName : 签名信息,保证token无法重复或者无法被篡改
public static String getToken(String userName){
// 创建Token构造器
JWTCreator.Builder builder = JWT.create();
//设置过期时间,设置什么时候过期:EXP_TIME计算一个过期的日子
Date date = new Date(System.currentTimeMillis() + EXP_TIME * 1000);
//创建token
String sign = builder.withExpiresAt(date)
.withClaim("userName", userName)
.withClaim("claimDate", new Date().getTime())
.sign(Algorithm.HMAC256(SECRET));
return sign;
}
/**
* 验证签名token
* @param token
* @return
*/
public static boolean verify(String token){
try{
DecodedJWT verify = JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
System.out.println("------- [token认证通过] ---------");
System.out.println("------- userName:"+verify.getClaim("userName")+" ---------");
System.out.println("------- 过期时间:"+verify.getExpiresAt()+" ---------");
return true;
}catch (Exception e){
return false;
}
}
}
3.创建一个拦截器拦截业务请求去验证token是否有效
- 编写一个验证token的拦截器
package com.webchartserver.core.intercetes;
import com.alibaba.fastjson.JSON;
import com.webchartserver.core.config.JWTUtil;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* 模块名称:是否登录过的拦截器(用于验证token是否有效)
* 模块类型:拦截器
* 编码人:高靖博
* 创建时间:2023/4/14
* 联系电话:18587388612
*/
public class LoginInterceptor implements HandlerInterceptor {
// 请求访问controller方法之前会指向性
/**
*
* @param request http请求对象
* @param response http响应对象
* @param handler 执行链路对象
* @return 返回值: true: 拦截器放行 false:拦截器阻止
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
// 所有的token都要放在请求头中 header
String token = request.getHeader("token");
//验证
if(token==null||token==""){
Map<String,String> json = new HashMap<>();
json.put("msg","认证失败,token不存在!");
json.put("code","-1");
PrintWriter writer = response.getWriter();
writer.write(JSON.toJSONString(json));
writer.flush();
writer.close();
return false;
}else{// 验证
boolean verify = JWTUtil.verify(token);
if(verify){
return true;
}else{
Map<String,String> json = new HashMap<>();
json.put("msg","认证失败!");
json.put("code","-1");
PrintWriter writer = response.getWriter();
writer.write(JSON.toJSONString(json));
writer.flush();
writer.close();
return false;
}
}
}
}
- 配置注册拦截器
规定那些地址会被拦截,哪些地址可以放行
package com.webchartserver.core.intercetes;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 模块名称:配置注册拦截器类
* 模块类型:配置类
* 编码人:高靖博
* 创建时间:2023/4/14
* 联系电话:18587388612
*/
@Configuration
public class InterceptConfig implements WebMvcConfigurer {
//注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册自定义的登录拦截器
InterceptorRegistration ic = registry.addInterceptor(new LoginInterceptor());
// 设置要拦截的路径有哪些
ic.addPathPatterns("/**"); // 所有请求服务端的路径地址都会被拦截
// 设置拦截器白名单
ic.excludePathPatterns(
"/user/login/**", // 登录接口不需要拦截
"/js/**", // 访问数据监控中心不需要拦截
"/css/**",
"/imgs/**",
"/druid/**",
"/v2/**",
"/webjars/**");
}
}
- 修改登录接口,当登录成功时从服务器返回一个token令牌
if(pwd_md5Hex.equals(pwdMD5)){ // 密码输入正确
result.setCode(200);
result.setMsg("登录成功!");
// 要返回token令牌到前端
String token = JWTUtil.getToken(user.getNickName());
result.setData(token);
postman测试
3. 好友列表的添加和历史信息的记录
1. 建表语句
CREATE TABLE `t_user_f` (
`infoID` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '记录主键',
`insertID` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '记录插入者帐号',
`insertIP` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '记录插入者IP',
`updateTime` datetime(0) NULL DEFAULT NULL COMMENT '记录更新时间',
`updateID` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '记录更新者帐号',
`updateIP` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '记录更新者IP',
`isDelete` int(11) NULL DEFAULT 0 COMMENT '是否删除',
`comment` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注',
`userID` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户id',
`fUserID` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '好友用户id',
`fNickName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '好友昵称(用户名)',
`fRealName` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '好友真实姓名',
PRIMARY KEY (`infoID`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `t_msg` (
`infoID` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '记录id',
`insertID` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '记录插入者帐号',
`insertIP` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '记录插入者IP',
`updateTime` datetime(0) NULL DEFAULT NULL COMMENT '记录更新时间',
`updateID` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '记录更新者帐号',
`updateIP` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '记录更新者IP',
`isDelete` int(11) NULL DEFAULT 0 COMMENT '是否删除',
`comment` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注',
`sendUserID` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '发送人的用户id',
`sendNickName` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '发送人的用户名',
`saveUserID` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '接收人的用户id',
`saveNickName` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '接收人的用户名称',
`msg` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '消息内容',
`saveAndSendTime` datetime(0) NULL DEFAULT NULL COMMENT '接收消息时间',
PRIMARY KEY (`infoID`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
使用mp代码生成
4. 实现聊天的敏感信息过滤
自定义工具类
package com.webchartserver.core.utils;
import cn.hutool.core.collection.ConcurrentHashSet;
import org.springframework.stereotype.Component;
import java.io.*;
/**
* 敏感信息过滤
* 2023/4/18
*/
@Component
public class FilterWordsUtil {
private FilterWordsUtil(){}
// 敏感词汇集合
private static ConcurrentHashSet<String> wordsSet = new ConcurrentHashSet<>();
// 初始化加载敏感词汇
static{
System.out.println("------------敏感词汇过滤工具加载-------------------");
loadTextFile();
}
/**
* 加载敏感词汇文件
*/
private static void loadTextFile(){
try {
InputStream resourceAsStream = FilterWordsUtil.class.getResourceAsStream("filterword.txt");
Reader reader = new InputStreamReader(resourceAsStream);
BufferedReader bufferedReader = new BufferedReader(reader);
String line;
while ((line = bufferedReader.readLine()) != null) {
wordsSet.add(line);
}
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 替换敏感字符
* @param words 原字符串
* @param replaceStr 替换的文本
* @return
*/
public static String changeWord(String words,char replaceStr){
for(String mgStr:wordsSet){
if(words.indexOf(mgStr)!=-1){
// 根据敏感词的长度生成*
int length = mgStr.length();
String repStr = "";
for(int i=0;i<length;i++){
repStr += replaceStr;
}
words = words.replaceAll(mgStr,repStr);
}
}
return words;
}
}
5.前端实现表情包的选择和发送
mysql配置可以存放unicode字符文字
[client]
default-character-set = utf8mb4
[mysql]
#设置mysql客户端默认字符集
default-character-set = utf8mb4
[mysqld]
#服务端使用的字符集默认为8比特编码的latin1字符集
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect='SET NAMES utf8mb4'
重启mysql服务