HMWZManager综合项目总结

一、项目介绍

开发环境:Nodejs平台+express框架+mysql数据库搭建服务端平台

功能:实现对英雄的增删改查,系统的注册、登录、会话状态保持等功能
效果图:
在这里插入图片描述
项目结构
在这里插入图片描述
大纲:
在这里插入图片描述

二、技术分析

  • express搭建服务器
  • 文件上传
  • 验证码功能
  • md5登录注册加密
  • cookie用户会话保持

三、项目流程

  • 1.express框架搭建服务器入口文件
    • 1.导入模块
    • 2.创建服务器
    • 3.配置中间件
      • 托管静态资源
      • body-parser:解析post请求参数
      • express-fileupload:文件上传
      • mysql-ithm:数据库操作
      • cookie-session中间件:用户会话保持
    • 4.开启服务器
  • 3.设计路由(接口文档)
  • 4.处理
    • 请求:获取请求参数
    • 处理:增删改查数据库
    • 响应:将数据库操作结果响应给客户端

express搭建服务器

//1.导入模块
const express = require('express');
//2.创建服务器
let app = express()

//3.配置中间件

//3.1 托管静态资源
app.use(express.static('www'));//静态网页
app.use(express.static('static'));//英雄图像

//3.2 body-parser:解析body
var bodyParser = require('body-parser');
// parse application/x-www-form-urlencoded
//以后所有的req都会有一个body属性,就是解析好的post参数对象
app.use(bodyParser.urlencoded({
    
     extended: false }));

//3.3 express-fileupload:接收文件数据
const fileUpload = require('express-fileupload');
app.use(fileUpload());

//3.4  mysql-ithm数据库操作
//(1)导包
const hm = require('mysql-ithm');
//(2)连接数据库
hm.connect({
    
    
    host: 'localhost',//数据库地址
    port: '3306',
    user: 'root',//用户名,没有可不填
    password: 'root',//密码,没有可不填
    database: 'cqmanager'//数据库名称
});
//(3)创建Model(表格模型:负责增删改查)
//英雄表格
let heroModel = hm.model('heros', {
    
    
    name: String,
    skill: String,
    icon: String,
});


//4.设计路由(接口文档)

//(1)查询英雄列表
app.get('/hero/list', (req, res) => {
    
    
});

//(2)查询英雄详情
app.get('/hero/info', (req, res) => {
    
    
});

//(3)编辑英雄
app.post('/hero/update', (req, res) => {
    
    
});

//(4)删除英雄
app.post('/hero/delete', (req, res) => {
    
    
});

//(5)新增英雄
app.post('/hero/add', (req, res) => {
    
    
});

//(6)验证码
app.get('/captcha', (req, res) => {
    
    
});

//(7)注册
app.post('/user/register', (req, res) => {
    
    
});

//(8)登录
app.post('/user/login', (req, res) => {
    
    
});

//(9)退出登录
app.get('/logout', (req, res) => {
    
    
});

//5.开启服务器
app.listen(3000, () => {
    
    
    console.log('success');
});

四、难点与注意点

  1. 服务端英雄查询注意点
    根据英雄名字查询数据库 需要使用sql的模糊查询
  • name like “%名字%” : name包含名字的
  • 不能使用 name = 名字 : 查询具体名字
  • str = name like "%${search}%";
//(1)查询英雄列表
app.get('/hero/list', (req, res) => {
    
    
    //1.请求 : 获取参数search
    let search = req.query.search;
    //2.处理:查询数据库
    let str = '';
    if (search) {
    
    
        //如果客户端传了search,则使用mysql模糊查询
        str = `name like "%${
      
      search}%"`;
    } else {
    
    
        //如果客户端没有传search,则查询hero表格所有字段
        str = ['name', 'skill', 'icon', 'id'];
    };
    heroModel.find(str, (err, results) => {
    
    
        //3.响应
        if (err) {
    
    
            res.send({
    
    
                code: 500,
                msg: err
            });
        } else {
    
    
            res.send({
    
    
                code: 200,
                heros: results,
            });
        }
    });

});
  1. 客户端代码:index.html
    注意点
  • 编辑按钮 :使用window.location.href 给编辑页面传参id
  • 删除按钮 :使用自定义属性 data-id 存储英雄id
  1. 服务端查询英雄详情
    • 数据库操作结果results一定是一个数组,如果想要返回客户端一个英雄对象,则需要取下标0
//(2)查询英雄详情
app.get('/hero/info', (req, res) => {
    
    
    //1.请求 : 获取参数id
    let id = req.query.id;
    //2.处理:查询数据库
    heroModel.find(`id=${
      
      id}`, (err, results) => {
    
    
        //3.响应
        if (err) {
    
    
            res.send({
    
    
                code: 500,
                msg: err
            });
        } else {
    
    
            res.send({
    
    
                code: 200,
                data: results[0]
            });
        }
    });
});
  1. 服务端编辑功能
    注意点:
  • 1.接收文件需要使用express-fileupload插件
  • 2.数据库只存储图片的路径字符串,具体的图片文件存储在static/imgs文件夹中
  • 3.图片文件名 使用 英雄名字.png 格式存储
//5.4 编辑英雄
app.post('/hero/update', (req, res) => {
    
    
    //1.请求
    //1.1 文本
    let {
    
     id, name, skill } = req.body;
    //1.2 文件
    let icon = req.files ? req.files.icon : null;

    console.log(id, name, skill, icon);

    //2.处理

    //2.1 处理图片
    if (icon) {
    
    
        icon.mv(`${
      
      __dirname}/static/imgs/${
      
      name}.png`, (err) => {
    
    
            if (err) {
    
    
                res.send({
    
    
                    code: 500,
                    msg: '修改失败'
                });
            };
        });
    };

    //2.2 处理文本
    let obj;
    if (icon) {
    
    
        obj = {
    
    
            name,
            id,
            skill,
            icon: `http://127.0.0.1:3000/imgs/${
      
      name}.png`
        };
    } else {
    
    
        obj = {
    
    
            name,
            id,
            skill,
        }
    };

    heroModel.update(`id=${
      
      id}`, obj, (err) => {
    
    
        if (err) {
    
    
            res.send({
    
    
                code: 500,
                msg: '修改失败'
            });
        } else {
    
    
            res.send({
    
    
                code: 200,
                msg: '修改成功'
            });
        }
    })



    // if( icon ){//有文件
    //     //2.1 文件:写入文件夹
    //     icon.mv(`${__dirname}/static/imgs/${name}.png`,(err)=>{
    
    
    //         if(err){
    
    
    //             res.send({
    
    
    //                 code:500,
    //                 msg:'修改失败'
    //             });
    //         };
    //     });

    //     //2.2 文本: 写入数据库   图片:数据库存对应的网址
    //     heroModel.update(`id=${id}`,{
    
    
    //         name,
    //         id,
    //         skill,
    //         icon:`http://127.0.0.1:3000/imgs/${name}.png`
    //     },(err)=>{
    
    
    //         if(err){
    
    
    //             res.send({
    
    
    //                 code:500,
    //                 msg:'修改失败'
    //             });
    //         }else{
    
    
    //             res.send({
    
    
    //                 code:200,
    //                 msg:'修改成功'
    //             });
    //         }
    //     })
    // }else{//没有文件
    //     heroModel.update(`id=${id}`,{
    
    
    //         name,
    //         id,
    //         skill
    //     },(err)=>{
    
    
    //         if(err){
    
    
    //             res.send({
    
    
    //                 code:500,
    //                 msg:'修改失败'
    //             });
    //         }else{
    
    
    //             res.send({
    
    
    //                 code:200,
    //                 msg:'修改成功'
    //             });
    //         }
    //     })
    // }

});
  1. 验证码功能
    验证码功能思路
  • 1.服务端生成一个二进制验证码图片文件与对应的验证码文本
  • 2.服务端声明全局变量存储验证码文本(用于客户验证)
  • 3.客户端img标签发起网络请求,服务端响应验证码图片
  • 4.客户端提交注册数据,服务端处理
    服务端代码

//(6)验证码
//声明全局变量存储验证码问题 (用于客户端验证)
let captchaTxt = '';
app.get('/captcha', (req, res) => {
    
    
    //1.创建验证码对象
    var captcha = svgCaptcha.create();
    //2.获取验证码文本并保存
    captchaTxt = captcha.text;
    console.log(captcha.text);
    //3.将验证码图片响应给客户端
    res.type('svg');
    res.status(200).send(captcha.data);
});

客户端register.html

  • img标签src属性请求图片,服务器响应之后img标签会自动加载图片
<img class="code" src="http://127.0.0.1:3000/captcha" alt="">

验证码不要直接比较,应该将服务器生成的验证码与用户上传的code全部转为大写或小写

  • 验证码一般不区分大小写

  • 验证码图片不能用ajax的,因为ajax请求json数据

    • 核心原理:重新设置一下img标签的src属性
      注意点: 浏览器有一个图片缓存机制。 如果是一样的请求,服务器只会在第一次请求的时候去获取图片。之后同一张图片请求会被浏览器忽略
      解决方案 : 添加一个随机参数,告诉浏览器这是不一样的请求就可以了。
  $('.code').click(function () {
    
    
        console.log('1111');
        //验证码图片不能用ajax的,因为ajax请求json数据
        $(this).attr('src',`http://127.0.0.1:3000/captcha?id=${
      
      Math.random()}`);

      });
//(7)注册
app.post('/user/register', (req, res) => {
    
    
    //1.获取post请求参数
    let body = req.body;
    console.log(body);
    //2.处理
    // code: 200 成功   401:用户已注册  402:验证码错误  500:服务器内部错误
    if (body.code.toLowerCase() != captchaTxt.toLowerCase()) {
    
     //全部转小写,不区分大小写
        //验证码错误
        res.send({
    
    
            code: 402,
            msg: '验证码错误'
        });
    } else {
    
    
        //检查是否已经注册
        userModel.find(`username="${
      
      body.username}"`, (err, results) => {
    
    
            if (err) {
    
    
                res.send({
    
    
                    code: 500,
                    msg: '注册失败'
                });
            } else if (results.length != 0) {
    
    
                res.send({
    
    
                    code: 401,
                    msg: '用户已存在'
                });
            } else {
    
     //如果没有注册,则添加到数据库
                userModel.insert({
    
    
                    username: body.username,
                    password: body.password
                }, (err, results) => {
    
    
                    if (err) {
    
    
                        res.send({
    
    
                            code: 500,
                            msg: '注册失败'
                        });
                    } else {
    
    
                        res.send({
    
    
                            code: 200,
                            msg: 'success'
                        });
                    }
                })
            }
        });
    }
});
  1. 会话保持


// 只要在响应头中加入 Set-Cookie 字段,客户端会把这个数据放到一个文件中然后保存到客户端电脑上
//服务器响应头添加cookie发给浏览器
  res.writeHead(200, {
    
    
    'Content-Type': 'text/plain; charset=utf-8',
    "Set-Cookie": 'userid=123456'
  })

  1. 退出登录重定向
    重定向技术实现退出登录流程
  • (1)客户端发送退出登录请求 : http://127.0.0.1:3000/logout
  • (2)服务器接收请求
    • a. 清空该用户cookie
    • b. 重定向刷新首页

html代码

<li><button class="btn btn-danger btn-exit" id="logout" onclick="location.href='/logout'">退出</button></li>

服务端

//(9)退出登录
app.get('/logout', (req, res) => {
    
    
    //1.清空session
    req.session = null;
    //2.重定向显示首页
    res.writeHead(302, {
    
    
        'Location': './index.html'
    });
    res.end();
});

五、总结

验证码技术

svg-captcha第三方模块文档传送门
安装

npm install --save svg-captcha

用法

const svgCaptcha = require('svg-captcha');
 
const captcha = svgCaptcha.create();
console.log(captcha);
// {data: '<svg.../svg>', text: 'abcd'}

在express使用

const svgCaptcha = require('svg-captcha');
 
app.get('/captcha', function (req, res) {
    
    
    var captcha = svgCaptcha.create();
    req.session.captcha = captcha.text;
    
    res.type('svg');
    res.status(200).send(captcha.data);
});
  • 工作流程
    在这里插入图片描述

md5 加密 加盐

数据加密思路

  • 1.客户端点击提交的时候对密码进行md5加密(使用前端第三方包 md5.min.js)
  • 2.服务端接收到密文保存到数据库
    • 密码明文只存在于用户填写的表单input中(不要在代码里面去打印用户明文,避免泄露)
    • 无论是网络传输还是服务器都只存储密文
      • 防止http请求被攻击导致密码泄露
      • 防止数据库被攻击导致密码泄露
    • 下一次用户登录的时候,使用相同加密方式对登录密码进行加密。然后服务端只匹配两个密文是否一致

导包

 <!-- 导入md5 -->
  <script src="./libs/md5.min.js"></script>

使用

  • 参数1:要加密的明文
  • 参数2:盐 作用:提高密文复杂度 同样的数据,盐不同得到的密文不同
  • 返回值:加密好的密文
let mima = md5( $('#password').val() ,'add salt');
console.log(mima);
  • 注意点
    前端不会直接去获取用户的密文,不安全。
    1)获取输入的数据
    (2)加密
    (3)直接赋值给表单
  $('#password').val( md5( $('#password').val() ,'add salt') );

登录时先转成密文

 $('#password').val( md5( $('#password').val() ,'add salt') );

cookie会话保持

  1. cookie的定义:Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)
  2. cookie的状态管理
  • HTTP 是无状态协议,它不对之前发生过的请求和响应的状态进行管理。也就是说,无法根据之前的状态进行本次的请求处理。
    • 任何浏览器都可以访问服务器,服务器根本不知道到底是哪一个用户访问自己
  1. 当一个用户第一次访问浏览器时,服务器会生成一个cookie,并且在响应头中添加cookie后返回给浏览器
  2. 浏览器会自动将cookie保存在下次,当下一次访问同一服务器时会自动将cookie放入请求头中发给服务器,这样服务器就可以识别用户
  • (1)每一个服务器都有自己的cookie(cookie的域名识别)
  • (2)cookie是由服务器生成的
    • 虽然浏览器本身也可以添加cookie,但是保持用户状态的cookie一定是由服务器生成的(seesion)
  • (3)浏览器访问同一域名的请求时会自动将cookie发给服务器,我们开发人员无需编写任何代码
    • 如果有则发送,没有则不发
    • cookie是在请求头中的
  • cookie与token区别
    在这里插入图片描述

cookie实现用户会话保持

文档
注意:不能使用跨域
安装

npm install cookie-session

express配置中间件

//4.4 cookie-session中间件 : 用户会话状态保持 (给req添加session属性)
const cookieSession = require('cookie-session');
app.use(cookieSession({
    
    
    name: '59qi',
    keys: [/* secret keys */'a','b'],//设置加密的钥匙
    // Cookie Options
    maxAge: 7 * 24 * 60 * 60 * 1000 // 7天 设置有效时间
}));

加入需要cookie的响应中

                res.send({
    
    
                    code: 200,
                    heros: results,
                    users:req.session.users
                });

猜你喜欢

转载自blog.csdn.net/weixin_44757417/article/details/108504452