✅ 作者简介:一名普通本科大三的学生,致力于提高前端开发能力
✨ 个人主页:前端小白在前进的主页
系列专栏 : node.js学习专栏
⭐️ 个人社区 : 个人交流社区
学习格言: ☀️ 打不倒你的会使你更强!☀️
在node系列的 ==>web开发模式 | express应用程序生成器 这篇文章中讲解了web开发模式,让大家认识到了前端的SSR和CSR,学习完开发模式后,我们这篇就衔接着开发模式来学习在交互过程中最重要的一部分
身份验证
,话不多说,正文开始!
身份认证(Authentication)又称“身份验证”、“鉴权”,是指通过一定的手段,完成对用户身份的确认
。
各大网站的手机验证码登录、邮箱密码登录、二维码登录
等。身份认证的目的,是为了确认当前所声称为某种身份的用户,确实是所声称的用户
。例如,你去找快递员取快递,你要怎么证明这份快递是你的
。
在互联网项目开发中,如何对用户的身份进行认证,是一个值得深入探讨的问题。例如,如何才能保证网站不会错误的将“马云的存款数额
”显示到“马化腾的账户
”上。(不得不说,黑马哥举的例子真不错)
对于服务端渲染
和前后端分离
这两种开发模式来说,分别有着不同的身份认证方案
:
服务端渲染(SSR)
推荐使用 Session 认证机制
前后端分离(CSR)
推荐使用 JWT 认证机制
了解 HTTP 协议的无状态性
是进一步学习 Session 认证机制的必要前提。
HTTP 协议的无状态性,指的是客户端的每次 HTTP 请求都是独立的
,连续多个请求之间没有直接的关系,服务器不会主动保留每次 HTTP 请求的状态
。
在这里,黑马哥给大家举了一个例子,超市的收银员去收钱的时候,不可能记得住每个用户是不是vip会员(这就是http的无状态性
),转到编程中去,就可以理解为浏览器不会保留你http请求的状态!
对于超市来说,为了方便收银员在进行结算时给 VIP 用户打折,超市可以为每个 VIP 用户发放会员卡。
注意
:现实生活中的会员卡身份认证方式
,在 Web 开发中的专业术语
叫做 Cookie
。
Cookie 是存储在用户浏览器中的一段不超过 4 KB
的字符串
。它由一个名称(Name
)、一个值(Value)
和其它几个用于控制 Cookie 有效期、安全性、使用范围
的可选属性组成。
不同域名下的 Cookie 各自独立,每当客户端发起请求时,会自动
把当前域名下
所有未过期的 Cookie
一同发送到服务器。
Cookie的几大特性
:
查看Cookie:
我们在这里以百度首页
为例子,(首先按F12
键)查看它的cookie:
客户端第一次请求服务器的时候,服务器通过响应头
的形式,向客户端发送一个身份认证的 Cookie,客户端会自动将 Cookie 保存在浏览器中
。
随后,当客户端浏览器每次请求服务器的时候,浏览器会自动
将身份认证相关的 Cookie,通过请求头的形式
发送给服务器,服务器即可验明客户端的身份。
由于 Cookie 是存储在浏览器中的,而且浏览器也提供了读写 Cookie 的 API
,因此 Cookie 很容易被伪造,不具有安全性
。因此不建议服务器将重要的隐私数据
,通过 Cookie 的形式发送给浏览器。
在这里,黑马又举了例子,真心不错,万一其他用户伪造了会员卡,该咋办呢?这就体现出来了Cookie的不安全性
注意
:千万不要使用 Cookie 存储重要且隐私的数据
!比如用户的身份信息、密码
等。
为了防止客户伪造会员卡,收银员在拿到客户出示的会员卡之后,可以在收银机上进行刷卡认证
。只有收银机确认存在的会员卡,才能被正常使用。
这种“会员卡 + 刷卡认证
”的设计理念,就是 Session 认证机制的精髓
。
在 Express
项目中,只需要安装 express-session 中间件
,即可在项目中使用 Session 认证
:
npm i express-session
express-session 中间件安装成功后,需要通过 app.use()
来注册 session 中间件
,示例代码如下:
const session = require('express-session')
app.use(
session({
secret: 'Bruce', // secret 的值为任意字符串
resave: false,
saveUninitalized: true,
})
)
如果你对其中的属性感到困惑的话可以来官方文档查看(全英)==>express-session中间件详解
中间件配置成功后,可通过 req.session
访问 session 对象
,存储用户信息
app.post('/api/login', (req, res) => {
req.session.user = req.body
req.session.isLogin = true
res.send({ status: 0, msg: 'login done' })
})
app.get('/api/username', (req, res) => {
if (!req.session.isLogin) {
return res.send({ status: 1, msg: 'fail' })
}
res.send({ status: 0, msg: 'success', username: req.session.user.username })
})
app.post('/api/logout', (req, res) => {
// 清空当前客户端的session信息
req.session.destroy()
res.send({ status: 0, msg: 'logout done' })
})
index页面:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="./jquery.js">script>
head>
<body>
<h1>首页h1>
<button id="btnLogout">退出登录button>
<script>
$(function () {
// 页面加载完成后,自动发起请求,获取用户姓名
$.get('/api/username', function (res) {
// status 为 0 表示获取用户名称成功;否则表示获取用户名称失败!
if (res.status !== 0) {
alert('您尚未登录,请登录后再执行此操作!')
location.href = './login.html'
} else {
alert('欢迎您:' + res.username)
}
})
// 点击按钮退出登录
$('#btnLogout').on('click', function () {
// 发起 POST 请求,退出登录
$.post('/api/logout', function (res) {
if (res.status === 0) {
// 如果 status 为 0,则表示退出成功,重新跳转到登录页面
location.href = './login.html'
}
})
})
})
script>
body>
html>
登录页面:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="./jquery.js">script>
head>
<body>
<form id="form1">
<div>账号:<input type="text" name="username" />div>
<div>密码:<input type="password" name="password" />div>
<button>登录button>
form>
<script>
$(function () {
// 监听表单的提交事件
$('#form1').on('submit', function (e) {
// 阻止默认提交行为
e.preventDefault()
// 发起 POST 登录请求
$.post('/api/login', $(this).serialize(), function (res) {
// status 为 0 表示登录成功;否则表示登录失败!
if (res.status === 0) {
location.href = './index.html'
} else {
alert('登录失败!')
}
})
})
})
script>
body>
html>
app.js文件
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// TODO_01:请配置 Session 中间件
const session = require('express-session')
app.use(session({
secret : 'wakaka', //secret 属性的值可以是任何字符串
resave:false, //固定写法,但是在官方文档中,建议使用true,但是通过http不会产生cookie,所以黑马哥用了false
saveUninitialized:true
}))
// 托管静态页面
app.use(express.static('./pages'))
// 解析 POST 提交过来的表单数据
app.use(express.urlencoded({ extended: false }))
// 登录的 API 接口
app.post('/api/login', (req, res) => {
// 判断用户提交的登录信息是否正确
if (req.body.username !== 'admin' || req.body.password !== '000000') {
return res.send({ status: 1, msg: '登录失败' })
}
// TODO_02:请将登录成功后的用户信息,保存到 Session 中
req.session.user = req.body
req.session.islogin = true
res.send({ status: 0, msg: '登录成功' })
})
// 获取用户姓名的接口
app.get('/api/username', (req, res) => {
// TODO_03:请从 Session 中获取用户的名称,响应给客户端
if(!req.session.islogin) {
return res.send({status :1,msg:'fail'})
}
res.send({ status:0,msg:'success',username:req.session.user.username })
})
// 退出登录的接口
app.post('/api/logout', (req, res) => {
// TODO_04:清空 Session 信息
req.session.destroy()
res.send({
status : 0,
msg : '退出登录成功'
})
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1:80')
})
Session更加偏向于在服务端渲染
中使用,可能在前后端交互
主流的时代中,使用很少,但是是不可或缺的,大家主要去理解session的工作机制,在实战开发中是很重要的,在面试中也是必问的,下一篇的JWT
是主流前后端分离
中用的身份验证机制
,希望大家持续关注更新哦!