[Project Combat] - SpringBoot+WebScoket+Vue+ElementUI をベースに、Web バージョンの Earth チャット ソフトウェアを実装します。

プロジェクト紹介

このプロジェクトはオープンソース化されています:

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.ダウンロードツール

アイデア ツール (エンタープライズ エディション) の
ダウンロード アドレス: https://www.jetbrains.com/idea/download/#section=windows を使用して
、クラックするクラッキング ツールを見つけます。

2.Maven ツールを構成する

ダウンロード アドレス: https://maven.apache.org/download.cgi
ここに画像の説明を挿入
1. ダウンロードして解凍し、環境変数を設定します
。mavne-"bin ディレクトリを設定し、パス環境変数に追加します。

ここに画像の説明を挿入
ここに画像の説明を挿入
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 プロジェクトをビルドします

ここに画像の説明を挿入ここに画像の説明を挿入
この時点でプロジェクト ウィンドウが開き、アイデアの Maven 構成を変更する必要があります。
ここに画像の説明を挿入

ここに画像の説明を挿入
完了後、プロジェクトは関連する jar パッケージを再ダウンロードし、しばらく待ってから
プログラムを開始します。
ここに画像の説明を挿入

2. ネットワーク インターフェイスを作成してテストする

コントローラークラスの作成

/**
 * 模块名称:测试控制器类
 * 模块类型: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の設定

リソース ディレクトリで、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統合データソース接続プール - Alibaba 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. vue スキャフォールディングを構築するための Npm、cnpm ノード環境

1.node.js環境をインストールする

1.公式サイトに入る

https://nodejs.org/ja/

2. msiをインストールし、ディレクトリをCドライブにインストールします

インストール後、cmd を使用して node-v npm -v を実行し、バージョン番号が出力されるかどうかを確認します。

3. Cドライブに加えてnodejsFileフォルダーを作成し、node_globalとnode_cacheの2つのフォルダーを作成して、次のコマンドを実行します。

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. パス変数で 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. axiosコンポーネント(ajaxコンポーネント)をvueプロジェクトにインストールします

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 コマンドを入力します: 表示言語、最初に英語を選択して再起動し、次に中国語を選択して再起動します。
  4. オープンターミナル
vue init webpack

「約束していません」というエラーが表示されます。vsCode ターミナル コンソールにはシステム書き込み権限がありません

このマシンに付属の cmd ウィンドウを使用して、管理者権限で起動します。

ディレクトリをプロジェクト パスにジャンプし、vue init webpack コマンドを実行します。

5. プロジェクトを開始する

npm 実行開発

ここに画像の説明を挿入

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 で暗号化する必要があります。MD5 は、
    hutool ツールキットの暗号化方式を導入して暗号化されます。
<!-- hutool工具 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.15</version>
        </dependency>
  1. レジスタコントローラメソッドの変更
@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ツールクラスの作成

役割: トークンの生成とトークン機能の検証

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. ビジネスリクエストをインターセプトしてトークンが有効かどうかを確認するインターセプターを作成します。

  1. トークンを検証するインターセプターを作成する

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. ログイン インターフェイスを変更し、ログインが成功したときにサーバーからトークン トークンを返します。
if(pwd_md5Hex.equals(pwdMD5)){
    
     // 密码输入正确
                result.setCode(200);
                result.setMsg("登录成功!");

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

郵便配達員のテスト
ここに画像の説明を挿入

ここに画像の説明を挿入

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