JavaScript之Node.js(四):使用Express操作SQL数据库与前后端身份认证

一、数据库的概念

组织、管理、存储数据的仓库。对数据库主要是增加、删除、修改、查询操作。
 
MySQL数据库,免费开源,
Oracle数据库,收费
SQL Server数据库,收费,
Mongodb数据库,
前三个是传统数据库,又叫关系数据库,或者SQL数据库,
最后 一个是新型数据库,又叫非关系数据库,或者NOSQL数据库,在一定程度上弥补了传统数据库的缺陷。
 
传统数据库组织结构
数据库、数据表、数据行、字段等4部分组成。
 
库、表、行、字段的关系
一般情况下,每个项目都有独立的数据;
不同的数据存储在不同的表中;
每个表中存储信息由字段决定;
表中的行代表一条具体数据;
 
相关MYsql相关软件
MySQL Server,存储数据存储和服务;
MySQL workbench,可视化MYSQL管理工具。
 

二、安装配置数据库

 
1、安装数据库
msi文件包含了数据库与一些相关包。
 
通过WorkBench,输入密码即可连接数据库。
 
2、创建数据库和表
进入workbench新建数据库,填写数据库名称,其它信息默认即可快速创建一个数据库;然后在该数据库下创建一个表并添加字段,设置字段类型时如果不能从下拉菜单中选择,则直接手动输入字段类型。其它选项如,PK主键,NN不能为空,NQ值唯一,AI值自动增长。
 
3、向表中写入数据
在表中单击右键,选择第一个命令,在弹出的界面输入表中字段的内容即可。
 
4、SQL是一门数据库编程语言;
使用SQL语言编写的代码叫做SQL语句;
SQL语言只能在关系数据库中使用。
 
5、增加,删除,修改、查询等等操作数据。创建数据库、表、存储过程、视图等。
 

三、MySQL数据库的使用

 
1、SQL语句对大小写不敏感
 
-- 1-1使用 * 查询表中所有列的内容
-- select * from users
-- 1-2使用列名查询指定列的内容,多个列之间使用逗号隔开
-- select username,password from users
-- 2-1 插入数据
-- insert into users (username,password) values('bailong','bailong123')
-- 3-1修改id为5的用户的密码为bl123
-- update users set password = 'bl123' where id = 5
-- 3-2 修改多个列的属性值,SET后面用逗号隔开即可
-- update users set password = 'shb123',status = '1' where id = 1
-- 4-1删除指定内容,一定要添加条件
-- delete from users where id =1
 
2、where子句
= <> > >= < <= between like
-- select * from users where  id = 5
-- select * from users where id <> 5
-- select * from users where id < 4
-- select * from users where id <= 4
-- select * from users where id > 3
-- select * from users where id >= 3
select * from users where username like 'twy'
 
3、and or 把两个或者多个条件结合起来
-- select * from users where id = 5 and status = 1
-- select * from users where id = 3 or status = 1
 
4、order by子句
-- 排序之升序,默认排序规则,或者使用关键字:asc
-- select * from users order by username asc
-- 排序之降序,关键字:desc
-- select * from users order by status desc
-- 多重排序,需要排序的字段用逗号隔开即可
-- select * from users order by status asc,username desc
 
5、count(*)统计总数
-- 统计总数 count(*)
-- select count(*) from users where status = 0
-- 给列起别名 as
-- select count(*) as total from users where status = 0
 

四、在Express中操作MySQL

 
1、安装配置SQL
//1.安装、配置mysql
const mysql = require('mysql')
const db = mysql.createPool({
    host: '127.0.0.1',
    user: 'root',
    password: 'root',
    database: 'my_db_01'
})
 
2、测试SQL是否正常工作
//2.测试mysql是否可以正常工作
db.query('select 1',(err,res) => {
    if(err) return console.log(err.message);
    console.log(res);
})
 
3、查询数据
//3.查询数据库中的数据,如果执行的是select语句,返回的是一个数组
db.query('select * from users',(err,res) => {
    if(err) return console.log(err.message);
    console.log(res);
})
 
4、插入数据
//4-1.插入数据普通方法
const user = {username: '京城小白龙',password: 'jc123'}
const sqlstr = 'insert into users (username,password) values(?,?)'
db.query(sqlstr,[user.username,user.password],(err,res) => {
    if(err) return console.log(err.message);
    if(res.affectedRows === 1) {console.log('插入数据成功');}
})
 
//4-2插入数据快捷方法
const user2 = {username: '京城小白龙2',password: 'jc123888'}
const sqlstr2 = 'insert into users set ?'
db.query(sqlstr2,user2,(err,res) => {
    if(err) return console.log(err.message);
    if(res.affectedRows === 1) {console.log('插入数据成功');}
})
 
5、更新数据
//5-1更新数据普通方式,定义更新数据
const user33 = {id: 5,username: 'A',password: 'B'}
//5-2定义SQL语句
const sqlstr33 = 'update users set username = ?,password = ? where id = ?'
//5-3执行SQL语句
db.query(sqlstr33,[user33.username,user33.password,user33.id],(err,res) => {
    if(err) return console.log(err.message);
    if(res.affectedRows === 1) {
        console.log('更新数据成功');
    }
})
 
//更新数据的快捷方式
const user33 = {id: 19,username: 'abc',password: 'def'}
const sqlstr33 = 'update users set ? where id = ?'
db.query(sqlstr33,[user33,user33.id],(err,res) => {
    if(err) return console.log(err.message);
    if(res.affectedRows === 1) {
        console.log('更新数据成功');
    }
})
 
6、删除数据
//6-1删除数据,从表中真正的删除数据,比较危险,不建议使用
const sqlStr = 'delete from users where id = ?'
//当只有一个问号?点位符时,可以活力数组,直接写值;当有多个问号?点位符时,必须用数据传递数值
db.query(sqlStr,16,(err,res) => {
    if(err) return console.log(err.message);
    if(res.affectedRows === 1) {
        console.log('删除成功');
    }
})
 
//6-2标记删除,是把status字段的值修改1,标记为删除,不是真正的删除,比较安全,建议使用
const sqlStr = 'update users set status = ? where id = ?'
db.query(sqlStr,[1,6],(err,res) => {
    if(err) return console.log(err.message);
    if(res.affectedRows === 1) {
        console.log('标记删除成功');
    }
})
 

五、前后端身份认证

 
web开发模式
 
基于服务端渲染的传统开发模式
服务器发送给客户端的HTML页面,是在服务器通过字符串拼接,动态生成。因此,客户端不需要使用ajax技术额外请求页面数据。优点是:前端耗时少,利于SEO。缺点是:占用服务器资源,不利于前后端分离。一般使用session认证。
 
基于前后端分离的开发模式
依赖于ajax技术的广泛应用,就是后端只负责API接口,前端调用接口的开发模式。
优点:开发体验好,前端侧重于体验,后端侧重于接口;减轻了服务器端的渲染压力。缺点:不利于SEO,利于SSR技术解决SEO问题。一般使用JWT认证。
 
不谈业务场景盲目的使用何种开发模式都是耍流氓。
企业站,使用利于SEO的服务器端渲染的开发模式;后台项目管理类项目,建议使用前后端分离。另外,如果两者都考虑,则建议首页用服务器端渲染,其它页面使用前后端分离。
 
HTTP协议无状态性
客户端的每次HTTP请求都是独立的,连续多个请求之间没有直接的关系,服务器不会主动保留每次HTTP请求的状态。
 
如何突破HTTP的无状态的限制
类似会员卡身份认证的方式,在WEB开发中的专业术语叫cookie。
 
存储在用户浏览器中的不超过4K的字符串,它由一个名称,一个值和其它用于控制cookie的有效期,安全性,使用范围等属性组成。
COOKIE是各自独立,用户访问请求时,会自动把当前域名下的所有未过期的COOKIE一同发送给服务器。
自动发送、域名独立、过期时限、4KB限制。
 
cookie在身份认证中的作用
客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的cookie,客户端会自动将cookie保留在浏览器中。
随后,当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的cookie,通过请求头部的形式发送给浏览器,服务器即可验明客户端的身份。
 
cookie不具有安全性
浏览器提供了读写cookie的API,因此,cookie很容易被伪造。不建议服务器将重要的隐私数据,通过cookie的形式发送给客户端浏览器。
 
提高身份认证的安全性
服务器端验证,确认存在即安全。
 
1、session工作原理
客户端与服务器之间,请求与响应的过程中,通过对比客户端中的cookie与服务器中的cookie,来验证用户身份。
 
在express中使用session中间件
 
// TODO_01:请配置 Session 中间件
const Session = require('express-session')
app.use(
  session({
    secret: 'bailong',
    resave: false,
    saveUninitialized: true
  })
)
 
  // TODO_02:请将登录成功后的用户信息,保存到 Session 中
  req.session.user = req.body
  req.session.islogin = true
 
  // TODO_03:请从 Session 中获取用户的名称,响应给客户端
  if(!req.session.islogin) {
    return res.send({
      status: 1,
      msg: 'fail'
    })
  }
  session.send({
    status: 0,
    msg: 'success',
    username: req.session.user.username
  })
})
 
  // TODO_04:清空 Session 信息
  req.session.destroy()
  res.send({
    status: 0,
    msg: '退出登陆成功'
  })
 
2、jwt在express生成token
 
// TODO_01:安装并导入 JWT 相关的两个包,分别是 jsonwebtoken 和 express-jwt
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
 
// TODO_02:定义 secret 密钥,建议将密钥命名为 secretKey
const secretKey = 'bailong.org.cn ^_^'
 
// TODO_04:注册将 JWT 字符串解析还原成 JSON 对象的中间件
// 注意:只要配置成功了 express-jwt 这个中间件,就可以把解析出来的用户信息,挂载到 req.user 属性上
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }))
 
// TODO_03:在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端
  // 参数1:用户的信息对象
  // 参数2:加密的秘钥
  // 参数3:配置对象,可以配置当前 token 的有效期
  // 记住:千万不要把密码加密到 token 字符中
  const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' })
  res.send({
    status: 200,
    message: '登录成功!',
    token: tokenStr, // 要发送给客户端的 token 字符串
  })
})
 
// 这是一个有权限的 API 接口
app.get('/admin/getinfo', function (req, res) {
  // TODO_05:使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端
  console.log(req.user)
  res.send({
    status: 200,
    message: '获取用户信息成功!',
    data: req.user, // 要发送给客户端的用户信息
  })
})
 
// TODO_06:使用全局错误处理中间件,捕获解析 JWT 失败后产生的错误
app.use((err, req, res, next) => {
  // 这次错误是由 token 解析失败导致的
  if (err.name === 'UnauthorizedError') {
    return res.send({
      status: 401,
      message: '无效的token',
    })
  }
  res.send({
    status: 500,
    message: '未知的错误',
  })
})