一、 前后端交互流程 1.前端发起请求 – 我应该知道 我要给什么 我想得到什么 2.服务器拦截请求 通过路由分发任务 3.分发给控制器 控制解析这个请求,拿到前端发送的参数 4.把操作数据库的东西发给给 数据持久的方法 5.拿到数据库操作后的结果 结果可能会有几种1.数据操作成功 2.数据操作失败 3.拿到数据 6.根据业务逻辑将数据库的结果返回前端 7.拿到后返响应给前端的数据 根据业务逻辑 渲染页面
二、 服务器文件夹 项目根目录 静态资源目录 public
路由 router
控制器 controller
数据持久化 modules
服务器配置 config
服务器主文件 app.js
三、 配置服务器 初始化服务器 npx express-generator --view=ejs 项目名
自定义配置服务器 安装nrm包 这个包是用来修改 npm源的 npm install 代表的意思是通过npm 安装包 npm install nrm -g 这里的-g代表全局安装 nrm test 测试所有的镜像源的网络延迟 nrm use taobao 将镜像源修改成淘宝
准备工作完毕 开始创建项目 步骤 初始化项目 npm init 命令初始化 初始化完 多了一个package.json 的文件 这个文件就是该项目的描述文件 类似与作者 项目依赖 2步骤下载 express框架 通过express 框架去搭建服务器 项目基础搭建完毕 需要开始写代码
下载组件框架及各个模块
npm i express morgan cookie-parser express-session sarve-favicon -s
1. app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 const express = require ('express' )const logger = require ('morgan' )const cookie = require ('cookie-session' )const session = require ('express-session' )const usersRouter = require ('/routers/usersRouter' )const path = require ('path' )const app = express ()app,use (logger ('dev' )) app.use (express.json ()) app.use (express.urlencoded ({extended :false })) app.use (cookie ()) app.use (session ({ secret :'nclksdnlck' , name : 'shop' , cookie : { maxAge :1000 *10 }, saveUninitialized : true , resave :false , rolling :true , })) app.use ('/拦截路径' ,(req,res,next )=> { if (req.session .sign ) { next () }else { res.redirect ('登陆路径' ) } }) app.use (express.static (path.join (__dirname,'静态资源文件夹' ))) app.use ('请求路径' ,userRouter) app.use ((req,res )=> { res.status (404 ) res.send ('404' ) }) app.use ((err,req,res,next )=> { res.status (500 ) res.json ({ err }) }) app.listen (端口号,()=> { console .log ('服务器启动成功' ) })
2. router
文件 userRouter.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const express = require ('express' )const router = express.Router ()const userCtrl = require ('控制器路径' )router.get ('/' ,(req,res )=> { res.send ('获取所有用户的信息' ) }) router.post ('请求路径' ,相应的控制器方法) router.get ('请求路径' ,相应的控制器方法) module .exports = router
3. ctrl
文件 userCtrl.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 const userModel = require ('模块路径' )const {passMd5,jiemi}module .exports = { async login (re1,res ) { const { userName, userPass } = req.body const result = await usersModel.login (userName) if (result.length == 0 ) { return resp.json ({ code : 1001 , msg :'账号密码错误' }) } const salt = result[0 ].l_pwd .substr (0 , 6 ) const newPassWord = jiemi (userPass, salt) if (result[0 ].l_pwd == newPassWord) { req.session .sign = true resp.json ({ code : 200 , msg :'ok' }) } else { resp.json ({ code : 1001 , msg :'账号或者密码错误' }) }, async register (req, resp ) { const { userName, userPass } = req.body const checkResult = await usersModel.check (userName) if (checkResult.length > 0 ) { resp.json ({ code : 1001 , msg :'当前用户已存在' }) return } const newPass = passMd5 (userPass) console .log ('加密之后的密码' ,newPass); const result = await usersModel.register (userName,newPass) resp.json ({ code : 200 , msg :'ok' }) }, }
4. Modules
文件 indexModule.js
数据库连接池配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 const mysql = require ('mysql' )const mysqlConfig = { host :'localhost' , port :3306 , user;'root' , password :'密码' , database :'表' } function query (sql,options ) { return new Promise ((resolve,reject )=> { let pool = mysql.createPool (mysqlConfig) pool.getConnection ((err,connection )=> { if (err) { reject (err) }else { connection.query (sql,options,(err,res )=> { if (err) { reject (err) }else { resolve (res) } }) } connection.release () }) }) }
userModules.js
控制器函数封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 module .exports = { check (userName ) { const sql = `select * from login where l_user = ?` return query (sql,[userName]) }, register (userName, password ) { const sql = `insert into login (l_user,l_pwd) values(?,?)` return query (sql,[userName,password]) }, login (userName ) { const sql = 'select * from login where l_user = ?' return query (sql,[userName]) } }
5. utils
文件 index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const crypto = require ('crypto' )function createRandom ( ) { const ran = Math .random ().toString ().substring (0 ,6 ) return ran } module .exports = { passMd5 (str ) { let md5 = crypto.createHash ('md5' ) let yan = createRandom () let result = md5.update (str+yan).digest ('hex' ) return yan + result }, jiemi (str,salt ) { let md5 = crypto.createHash ('md5' ) let result = md5.update (str+salt).digest ('hex' ) return salt + result } }
四、模块说明 1. 加密处理 前端加密可以防止直接传输用户的原始密码。
后端加密防止密码泄露、撞库。
加密方法:
哈希算法、HMAC
算法、签名、对称性加密算法、非对称性加密算法。 有些加密后的信息是不可逆的,这种算法就只能暴力破解(撞库),比如 md5. md5
加密:nodejs
内置一个crypto
加密模块
1 2 3 const crypto = require ('crypto' )let md5 = crypto.createHash ('md5' )let result = md5.update (加密的信息).digest
2. 注册 3. 登陆 前端:将用户输入的数据传给后端 后端:查数据,根据userName
查询对应的数据 将数据库中的密码去除,截取前6为,得到盐 然后用相同的加密方法得到用户登陆时的输入的密码md5 将盐+密码与数据库中的密码比较 4. 登陆凭证 当访问受保护的页面的时候,若用户没有登陆或者权限不够,则不能能够访问,强制重定向到登录页面。
会话:
会话是指用户与系统进行通讯的过程,一般应用于网络,而网络是基于 HTTP 通信的。
而 HTTP 协议是一种无状态的协议,1次请求结束后,连接就会断开,而不会去维护连接时所传输的信息。 就是说当你登陆成功后,下一次请求后端,后端无法识别是否是上一次的用户。
所以为了解决这个问题,需要通过一些手段,在用户登录成功后,去记录登录信息,那么下一次请求的时候,可以凭借这个登录信息去识别用户。
会话的几种方式
session & cookie token(jwt) 三方登陆 4.1、cookie 概念:存储在浏览器本地的缓存数据,每次浏览器在发起请求时,都会自动携带本站的cookie数据。
优缺点:
速度块
安全性比较低,存储数据量较小
存储位置:浏览器
使用:
1 2 3 4 5 npm i cookie-parser const cookie = require ('cookie-parser' )app.use (cookie ())
4.2、session 概念:在服务端存储用户信息
优缺点:
4.3、工作原理 浏览器带着 用户名 和 密码 访问 /login;
服务器端验证 用户名 和 密码 正确后,那么会通过 session 生成会话信息,该信息保存在 session 中;
然后服务端将 session 的 sessionId
返回给浏览器存储在 cookie 中;
之后浏览器的每一次请求,都会自动携带 cookie 信息的;
服务器从请求中获取 cookie 信息,将得到的 sessionId
与 seesion
中的数据比对,如果存在则登录,正常返回数据,如果不存在则不给前端返回数据。
5. app.use
app.use([path], callback, [callback...])
当请求路径匹配的时候就会执行后面的回调函数。路径的默认值是 /,是针对于每一个请求路径的。
path:路径,默认 /,可选参数;
callback:回调函数,有四个参数 ** 四个参数,依次为:err, req, res, next
** 三个参数,依次为:req, res, next
** 两个参数,依次为:req,res
next: 是一个函数,默认情况下,路径匹配后,会执行 app.use
的 callback,执行后当前程序就终止掉。如果调用 next 函数,那么程序就会向下传递,下一个匹配路径的中间件也会执行。
6. 路由拦截 需求: 网站中受保护的页面和请求很多,如何一次性进行判断?
关键: 如何判断哪些路由是受保护的?(判断某一个路径不能访问)
规定: 以 /protected 开头的路径被保护起来
处理:
受保护的页面统一放在 public/protected 文件夹内; 受保护的接口,统一以 /protected 开头; 7. 数据库连接池 场景: 假如没有连接池,那么我们在使用JDBC
程序的时候,就会反复的创建连接,销毁连接,这样做比较消耗资源,并不能做到连接的反复利用。这样做如果这个程序的用户人数很多,那么对性能的影响就会很大。
处理: 所以为了优化性能,就需要自己去创建一个连接池,然后每次从连接池里面获取连接。使用完了连接,放回连接池,做到连接的反复使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 const mysqlConfig = { host : 'localhost' , port : 3306 , user : 'root' , password : 'l19980907' , database :'demo3' } function query (sql, options ) { return new Promise ((resolve, reject ) => { let pool = mysql.createPool (mysqlConfig) pool.getConnection ((err, connection ) => { console .log ('连接池' , connection); if (err) { reject (err) } else { connection.query (sql, options, (err, res ) => { if (err) { reject (err) } else { resolve (res) } }) } connection.release () }) }) } module .exports = query