[Project Combat] - Based on SpringBoot+WebScoket+Vue+ElementUI to implement a web version of the earth chat software

Project Introduction

The project has been open source gitee:

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

Technology selection

Backend: SpringBoot (WEB) + JWT + MyBatis-plus +MySql5.7 + Redis + SpringFileStroage
Frontend: Vue2.0 + ElementUI

Technical Difficulties

  1. Multi-terminal chat (implementation of long links) - WebSocket
  2. File storage, message storage - IO\Reids\Mysql
  3. Verification of login authority (full version login function) - JWT\behavior verification code\interceptor, filter (SpringBoot)

The construction of the back-end framework

1. SpringBoot framework construction

1. Download tool

Use the idea tool (Enterprise Edition)
download address: https://www.jetbrains.com/idea/download/#section=windows
to find a cracking tool to crack

2. Configure the maven tool

Download address: https://maven.apache.org/download.cgi
insert image description here
1. Download and decompress and configure the environment variable:
configure mavne-"bin directory and add it to the path environment variable

insert image description here
insert image description here
2. Modify the maven configuration:
insert image description here

<?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 builds the springBoot project

insert image description hereinsert image description here
At this point, the project window will open, and then the maven configuration of the idea needs to be modified
insert image description here

insert image description here
After completion, the project will re-download the relevant jar package, wait for a while and
then start the program:
insert image description here

2. Write and test the network interface

Controller class writing

/**
 * 模块名称:测试控制器类
 * 模块类型: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();
    }

}

insert image description here

3. SpringBoot integrates MyBatis-plus

1. Open pom.xml to introduce dependencies

<!-- 新:版本升级 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. Configure mybatis-plus

In the resources directory, delete the application.properties file and change it to the application.yml file

# 修改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. Write a code generation tool

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. Prepare a data table for code generation

User Information Form

User Information Form-ER Diagram
insert image description here

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 integrated data source connection pool - Alibaba druid

The role of the database connection pool:

  1. Reduce the io process of creating connections
  2. Simpler way to use connections and easier to manage
  3. Satisfy the business of repeated reading and writing to create connections in high concurrency scenarios

1. Introduce dependencies

<!-- 数据源依赖 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

2. Configure druid connection pool in application.yml

# 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:

Start the project and test whether the druid connection pool is normal:

http://localhost:9010/druid/index.html

4. Low-code CRUD of SpringBoot+MP

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 integrates WebSocket

1. Introduce webSocket dependency

<!-- 服务端WebSocket需要的依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

2. Write a WebSocket service configuration class

Build a webSocket server

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();
    }

}

Create a business class for message forwarding

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异常!!!!");
    }


}


Front-end testing

<!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>

Front-end framework construction

1. Npm, cnpm node environment to build vue scaffolding

1. Install node.js environment

1. Enter the official website

https://nodejs.org/en/

2. Install msi, install the directory to the C drive

After installation, use cmd to execute node- v npm -v to see if the version number is output

3. Create a nodejsFile folder in addition to the C drive, create two folders node_global and node_cache, and then run the following command

npm config set prefix "D:\Program Files\nodejs\node_global"
npm config set cache "D:\Program Files\nodejs\node_cache"

4. Check whether the path path is called C:\ProgramFiles\nodejs\node_modules in the current system variables

If there is, create a drive letter with the path folder of the same name outside the C drive, if not, don’t worry about it

  1. Configure your C:\Program Files\nodejs\node_modules path in the path variable

2. Install Taobao mirror cnpm

Function: Same as npm, npm is downloaded from overseas mirror warehouse, cnmp is downloaded from Ali Taobao mirror warehouse

npm install -g cnpm --registry=https://registry.npm.taobao.org

After the installation is complete, use: cnpm -v to view the version

3. Install webpack

npm install webpack -g

After the installation is complete, use: npm webpack -v to view the version

4. Download vue scaffolding

1. Download scaffolding resources

cnpm install -g [email protected]

2. Go to your gloab directory to see if there are files starting with vue

insert image description here

  1. Create an empty folder under another folder, press and hold shift + right mouse button in the folder, click here to open the pwershell window
  1. Open the window and enter: vue init webpack

5. Initialize and build vue scaffolding

insert image description here
insert image description here

6. Run the vue project

Press and hold shift + right mouse button in the project root directory, click here to open the pwershell window, and execute npm run dev

2. Install the axios component (ajax component) in the vue project

1. Use cmd to execute in the project directory

//1. 在项目窗口中执行
cnpm install axios

insert image description here
On behalf of the successful installation

2. main.js configuration import related components

import axios from 'axios'
Vue.prototype.$axios = axios //全局属性

3. Write a request tool class

Role: Encapsulate code about http request and response processing

// 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. Set up cross-domain request issues

insert image description here
Configure a proxy registration tool in vue

insert image description here

3. Use VSCode to build Vue project

Download VsCode

https://code.visualstudio.com/

Component download

insert image description here
insert image description here

Create a vue project

  1. Create an empty project folder
  2. Open this folder with vsCode
  3. Set vsCode language: ctrl+shift+P Input command: Display language, first select English to restart, then select Chinese and then restart.
  4. open terminal
vue init webpack

An error will appear: not promise. . . . The vsCode terminal console does not have system write permissions

Use the cmd window that comes with this machine to start with administrator privileges

Jump the directory to the project path and execute the vue init webpack command

5. Start the project

npm run dev

insert image description here

6. Quit running

ctrl+c

4. Write the first vue file

<!-- Dom页面 -->
<template>
	<!-- 所有的页面元素都要卸载div之内 -->
  <div id='UserIndex'>
    
  </div>
</template>

<!-- 组件js -->
<script>
export default {
    name:'UserIndex',
    data(){

    }
}
</script>

<!-- CSS代码 -->
<style scoped>

</style>

Create a custom code snippet with VSCode

File – "Preferences - "Configure User Code Snippets

{
    
    
	"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"
	}
}

business realization part

1. User registration and login

insert image description here

1. User registration function

backend code

  1. The backend receives the registration information, and the password needs to be encrypted with MD5, which is
    encrypted by introducing the encryption method of the hutool toolkit
<!-- hutool工具 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.15</version>
        </dependency>
  1. Register controller method modification
@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. User login function

backend code section

The front-end passed the plaintext password, and the plaintext password needs to be encrypted and then compared with the database with the ciphertext

@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. Realize system authentication function

Verify whether the user has completed the login, and only after the user logs in normally can he access the interface in the system
insert image description here

1. The implementation method uses the JWT framework

1. Introduce jwt dependency

<dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.19.0</version>
        </dependency>

2. Create a JWT tool class

Role: generate token and verify token function

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. Create an interceptor to intercept business requests to verify whether the token is valid

  1. Write an interceptor that verifies the 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. Configure the registration interceptor
    to specify which addresses will be blocked and which addresses can be released
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. Modify the login interface and return a token token from the server when the login is successful
if(pwd_md5Hex.equals(pwdMD5)){
    
     // 密码输入正确
                result.setCode(200);
                result.setMsg("登录成功!");

                // 要返回token令牌到前端
                String token = JWTUtil.getToken(user.getNickName());
                result.setData(token);

postman test
insert image description here

insert image description here

3. Adding friends list and recording historical information

1. Create table statement


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;

Use mp code generation

4. Realize the filtering of sensitive information in chat

custom tools

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;
    }

}

insert image description here

5. The front end realizes the selection and sending of expression packs

mysql configuration can store unicode character text

[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'

Restart the mysql service

Guess you like

Origin blog.csdn.net/gjb760662328/article/details/130124803