【项目实战】- 基于SpringBoot+WebScoket+Vue+ElementUI实现一个网页版地球聊天软件

项目介绍

项目已开源gitee:

https://gitee.com/gdones/gd-webchart

技术选型

后端:SpringBoot(WEB)+ JWT + MyBatis-plus +MySql5.7 + Redis + SpringFileStroage
前端:Vue2.0 + ElementUI

技术难点

  1. 多端聊天(长链接的实现)- WebSocket
  2. 文件存储、消息的存储 - IO\Reids\Mysql
  3. 登录权限的验证(完整版登录功能)- 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

数据库连接池的作用:

  1. 减少创建连接的io过程
  2. 使用连接的方式更简单,管理也更加简单
  3. 满足高并发场景下重复读写创建连接的业务

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盘之外的盘符,如果没有就不用管了

  1. 在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 [email protected]

2.去到你的gloab目录查看是否有vue开头的文件

在这里插入图片描述

  1. 到别的文件夹下创建一个空的文件夹,在文件夹中按住shift+鼠标右键,点击在此处打开pwershell窗口
  1. 打开窗口输入: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项目

  1. 创建一个空的项目文件夹
  2. 使用vsCode打开这个文件夹
  3. 设置vsCode语言:ctrl+shift+P 输入指令:Display language 先选择英文重启后再选择中文再重启就好了
  4. 打开终端
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.用户注册功能

后端部分代码

  1. 后端接收到注册信息,需要将密码进行MD5加密
    引入hutool工具包加密方法加密
<!-- hutool工具 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.15</version>
        </dependency>
  1. 注册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是否有效

  1. 编写一个验证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;
            }
        }
    }
}
  1. 配置注册拦截器
    规定那些地址会被拦截,哪些地址可以放行
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/**");

    }
}

  1. 修改登录接口,当登录成功时从服务器返回一个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服务

猜你喜欢

转载自blog.csdn.net/gjb760662328/article/details/130124803