Web开发模式
客户端渲染前后端分离
前后端分离的概念前后端分离的优缺点 如何选择 Web 开发模式 身份认证
不同开发模式下的身份认证 Session 认证机制
HTTP 协议
无状态性如何突破 cookie
cookie简介
cookie概念cookie在身份认证中的作用cookie不具有安全性 在 express 中使用 cookie-parser 中间件
1.下载2.配置3.使用 Session
session工作原理在`express`中使用 `express-session` 中间件
1.安装2.配置3.存数据4.读数据5.清空数据 Session局限性 JWT认证机制
JWT相关知识
1.JWT工作原理2.JWT组成部分3.JWT使用方式 在 express 中使用 JWT
1.安装相关包2.导入相关包3.定义 secret 密钥4.登录后生成 token5.将 token 字符串解析为 JSON 对象6. demo 代码报错总结:
Cannot set headers after they are sent to the clientError: algorithms should be set一直报`UnauthorizedError`
Web开发模式 客户端渲染优点:
前端耗时少。因为服务器端负责动态的生成 HTML 内容,浏览器只需要直接渲染页面即可。尤其是移动端,更加省电
有利于SEO。因为服务端响应是完整的 HTML 页面内容,所以爬虫更容易爬取获得信息,更利于SEO(搜索引擎优化)。
缺点:
占用服务器资源。服务器来完成页面的内容拼接,如果请求太多,会对服务器造成一定的访问压力。不利于前后端分离开发效率低。使用服务端渲染,则无法进行分工合作,尤其对于前端复杂度较高的项目,不利于项目高效开发。 前后端分离 前后端分离的概念
前后端分离的开发模式,依赖Ajax技术的广泛应用。简而言之,前后端分离的Web开发模式,就是后端只负责提供API接口,前端使用Ajax调用接口的开发模式。
前后端分离的优缺点优点:
开发体验好。前端专注于UI页面的开发,后端专注于api的开发,且前端有更多的选择性。用户体验好。Ajax技术广泛应用,极大的提高了用户的体验,可以轻松的实现页面的局部刷新。减轻了服务器端的渲染压力。
缺点:
不利于SEO。因为完整的页面需要在客户端动态拼接完成,所有爬虫对无法爬取的页面的有效信息。(解决方案:利用Vue ,React 等前端框架的 SSR 技术能够很好的解决。) 如何选择 Web 开发模式
比如企业级的网站,主要功能是展示而没有复杂的交互,并且需要良好的SEO,则这是我们就需要使用服务器端渲染。而类似后台管理项目,交互性比较强,不需要考虑SEO,那么就可以使用前后端分离的开发模式。 身份认证
身份认证指通过一定的手段,完成对用户身份的确认。
不同开发模式下的身份认证服务端渲染推荐使用Session认证机制前后端分离推荐使用JWT认证机制 Session 认证机制 HTTP 协议 无状态性
HTTP协议的无状态性,指的是客户端的每次HTTP请求都是独立的,连续多个请求之间没有直接的关系,服务器不会主动保存每次请求的状态。 如何突破
使用 cookie cookie cookie简介 cookie概念
cookie是储存在用户浏览器中一段不超过4KB的字符串。它由一个名称(Name),一个值(Value)和其他几个用于控制cookie有效期,安全性,使用范围的可选属性组成。
不同域名下的cookie各自独立,每当客户端发起请求的时候,会自动把当前域名下的所有未过期的cookie一同发送到服务器。
总结cookie的几大特性:
自动发送域名独立过期时限4KB限制 cookie在身份认证中的作用
客户端在第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的 cookie ,客户端会自动将cookie保存在浏览器中。随后,当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的 cookie ,通过请求头的形式发送给服务器,服务器即可检验客户端身份。 cookie不具有安全性
由于cookie是储存在浏览器中的,而且浏览器也提供了读写cookie的API,因此cookie很容易被伪造,不具有安全性,因此不建议服务器将重要的隐私数据,通过cookie的形式发送给浏览器。
在 express 中使用 cookie-parser 中间件 1.下载npm install cookie-parser2.配置
var cookieParser = require('cookie-parser')
3.使用
// 根据是否记住来设置cookie
// 这里只记录邮箱 隐私的信息不宜记录在cookie里
if(req.body.remember === 'on'){
res.cookie('user' , {
email: req.body.email,
},{
// 设置记住时间为七天
maxAge: 7*24*60*60*1000
})
}else{
// 如果这次没有设置记住 清理 user 的 okie
res.clearcookie('user')
}
Session
session工作原理
在express中使用 express-session 中间件
1.安装
在 Express 项目中,只需要安装 express-session 中间件,即可在项目中使用 Session 认证:
npm install express-session2.配置
安装完成后,需要通过require来导入,再通过app.use()来注册session中间件:
// 导入
var session = require('express-session')
// 配置 (路由之前)
app.use(session({
secret: 'myKeyBoard', //配置加密字符串
resave: false, //固定写法
saveUninitialized: true //固定写法
}))
3.存数据
通过req.session来访问和使用session对象:
// 登录成功 通过Session记录登录状态 req.session.user = data4.读数据
通过req.session.xxx来读数据:
//通过是否登录来渲染首页
router.get('/' , function(req , res, next){
if(req.session.user){
User.findOne({
email: req.session.user.email
}).then(function(data){
res.render('index.html' , {
user: data,
})
})
}else{
res.render('index.html' , {
user: null
})
}
})
5.清空数据
通过将指定数据设置为null或者使用 req.session.destroy() 函数:
注意: req.session.destroy() 只会清空当前用户的session,每个用户之间的session独立。
// 处理退出请求
router.get('/logout' , function(req , res){
// 清除登录状态
req.session.user = null //将指定数据设置为`null`
// req.session.destroy() // 或者使用 req.session.destroy() 函数
// 刷新当前页面
// a 标签是同步请求 服务端可以重定向
res.redirect('/')
})
Session局限性
Session认证机制需要配合cookie才能实现。由于cookie默认不支持跨域访问,所以,当涉及到前端跨域请求后端接口的时候,需要做很多额外的配置,才能实现跨域Session认证。此时推荐JWT身份认证。
JWT认证机制 JWT相关知识 1.JWT工作原理
用户的信息通过token字符串的形式,保存在客户端浏览器中。服务器通过还原token字符串的形式来认证用户的身份。
通常由三部分组成,分别是Header(头部),Payload(有效荷载),Signature(签名)。
Payload是真正的用户信息,是用户信息加密过后生成的字符串。
Header和Signature是安全性相关的部分,只是为了保证Token的安全性。
三者使用英文分隔符.隔开,格式如下:
Header.Payload.Signature3.JWT使用方式
客户端收到服务器返回的JWT之后,通常将它储存在localStorage或sessionStorage中。
此后,客户端每次与服务器通信,都要带上这个JWT字符串,从而进行身份验证。
推荐的做法是把JWT放在HTTP请求头的Authorization字段中,格式如下:
Authorization: Bearer在 express 中使用 JWT 1.安装相关包
npm install jsonwebtoken express-jwt
jsonwebtoken用于生成jwt字符串express-jwt用于将jwt字符串解析还原成json对象 2.导入相关包
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
3.定义 secret 密钥
为了保证JWT字符串的安全性,防止JWT字符串再网络传输过程中被别人破解,我们需要专门定义一个用于加密和解密的secret密钥。
当生成JWT字符串的时候,需要使用secret密钥对用户的信息进行加密,最终得到加密好的JWT字符串。当把JWT字符串解密还原成JSON对象的时候,需要使用secret密钥进行解密。
const secretKey = 'mySecretKey'4.登录后生成 token
jsonwebtoken包提供sign()方法,将用户的信息加密成JWT字符串,响应给客户端:
参数1:用户的信息对象参数2:加密密钥参数3:配置对象,可配置token有效期
app.post('/api/post' , function(req , res){
console.log(req.body)
//省略了登录失败的 code ...
res.send({
status:200,
message: '登录成功',
token:jwt.sign({
foo: 'bar',
} , secretKey ,{
//设置token的有效期
//'xxs' xx秒
//'xxh' xx小时
expiresIn: '30s'
})
})
})
5.将 token 字符串解析为 JSON 对象
expressJWT({secret: secretKey})解析 Token 的中间件.unless({path : [/^/api//]}) 用来指定哪些接口不需要访问权限(可以自己写正则 ,多个放在数组中用逗号隔开)配置好后会将解析出来的数据直接挂载到req.user 属性上
// expressJWT({secret: secretKey}) 解析 Token 的中间件
// .unless({path : [/^/api//]}) 用来指定哪些接口不需要访问权限(自己写正则)
// 将解析出来的数据直接挂载到 req.user 上
app.use(expressJWT({secret: secretKey , algorithms: ['HS256']}).unless({path : [/^/api//]}))
6. demo 代码
app.js
const express = require('express')
const bodyParser = require('body-parser')
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
const cors = require('cors')
const secretKey = 'mySecretKey'
var app = express()
// 解决跨域
app.use(cors())
//配置解析 post
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
// expressJWT({secret: secretKey}) 解析 Token 的中间件
// .unless({path : [/^/api//]}) 用来指定哪些接口不需要访问权限(自己写正则)
// 将解析出来的数据直接挂载到 req.user 上
app.use(expressJWT({secret: secretKey , algorithms: ['HS256']}).unless({path : [/^/api//]}))
app.post('/api/login' , function(req , res){
return res.send({
status:200,
message: '登录成功',
token:jwt.sign({
foo: 'bar',
} , secretKey , { expiresIn: '30s' })
})
})
app.post('/admin' , function(req , res , next){
console.log(req.user)
return res.send({
status:200,
message: 'token有效',
})
})
// 配置一个全局错误处理中间件
app.use(function(err, req, res, next){
// token解析失败导致的错误
if(err.name === 'UnauthorizedError') {
return res.status(401).json({
err_code: 500,
message: '无效的token'
})
}
// 其他原因导致的错误
return res.status(500).json({
err_code: 500,
message: err.message
})
})
app.listen(5000 , function() {
console.log('Running...')
})
前端 index.js 页面
document
报错总结:
Cannot set headers after they are sent to the client
原因:客户端发送一次请求的时候,服务器端给出了多次响应
解决:每次响应后添加return
Error: algorithms should be set解决:解密加入解密算法algorithms: ['HS256']
app.use(expressJWT({secret: secretKey , algorithms: ['HS256']}).unless({path : [/^/api//]}))
一直报UnauthorizedError
有可能是token字符串设置有效期过短导致解析时发现token已经过期
还有可能是前端页面发送的token格式有误
正确的格式:
// 请求头 空格 token字符串 Authorization: Bearer
express-jwt源码
该部分源码来自链接.
// 从options中获取token
if (options.getToken && typeof options.getToken === 'function') {
try {
token = options.getToken(req);
} catch (e) {
return next(e);
}
//从authorization中获取token
} else if (req.headers && req.headers.authorization) {
// 根据空格来划分
var parts = req.headers.authorization.split(' ');
if (parts.length == 2) {
var scheme = parts[0];
var credentials = parts[1];
if (/^Bearer$/i.test(scheme)) {
// 如果前部分是 'Bearer' 则后半部分是token
token = credentials;
} else {
if (credentialsRequired) {
return next(new UnauthorizedError('credentials_bad_scheme', { message: 'Format is Authorization: Bearer [token]' }));
} else {
return next();
}
}
//如果以上俩种方法都得不到有效的 token 则报错
} else {
return next(new UnauthorizedError('credentials_bad_format', { message: 'Format is Authorization: Bearer [token]' }));
}
}



