这意味着来自客户端的每个HTTP请求都是独立的,多个连续请求之间没有直接关系,并且服务器不会主动维护每个HTTP请求的状态。例如,超市收银员无法记住哪些顾客是VIP 顾客或不是VIP 顾客。如何克服HTTP的无状态限制
根据上面的例子,可以为每个VIP客户发行一张会员卡,通过会员卡可以识别用户的身份。在网络开发术语中,它们被称为cookie。什么是cookie
Cookie 是存储在用户浏览器中的小于4KB 的字符串。它由名称、值和其他几个可选属性组成,用于控制cookie 的生命周期、安全性和使用范围。
不同域名下的cookie是独立的。当客户端发起请求时,当前域名中所有未过期的cookie都会自动发送到服务器。
Cookie 的主要特征是:
自动发送过期限制与域名4KB限制无关
cookie在身份认证中的作用
当客户端第一次请求服务器时,服务器会以响应头的形式向客户端发送身份认证cookie,客户端会自动将该cookie保存在自己的浏览器中。然后,每当客户端浏览器向服务器发出请求时,浏览器都会自动以请求头的形式向服务器发送一个与身份认证相关的cookie,以便服务器验证客户端的身份。 (编码时无需执行任何操作)Cookie 不安全
由于cookie存储在浏览器中,而浏览器也提供了读写cookie的API,因此cookie很容易伪造,而且不安全。因此,我们不建议我们的服务器以cookies的形式向您的浏览器发送敏感的个人数据。举例:如果超市通过会员卡识别用户的VIP身份,则顾客可以伪造会员卡并造成受潮损坏。提高身份认证的安全性
为防止顾客伪造会员卡,顾客出示会员卡后,收银员可到收银台进行身份验证。只有在收银台确认存在的会员卡才能正常使用。这种“会员卡+信用卡认证”的设计理念就是Session认证机制的精髓。会议如何运作
首先,客户端浏览器将账户密码发送到服务器。然后服务器验证帐户密码。登录成功后的用户信息将被存储在服务器的内存中,并生成相应的cookie字符串并响应给客户端。客户端浏览器会自动保存当前域名的cookie。当客户端再次发起请求时,当前域名的所有可用cookie都会通过请求头自动发送到服务器。服务器根据请求头中的cookie从内存中取出对应的用户信息,并在验证用户身份成功后,为当前用户生成具体的响应内容并响应给浏览器。
前后端的身份认证
安装快速会话中间件
Express项目只需要安装Express-session中间件就可以使用会话认证。
npm 安装快速会话
配置Express 会话中间件
中间件安装成功后,需要通过app.use()注册会话中间件。示例代码如下。
//1.导入会话中间件
var session=require(\’express-session\’)
//2.配置会话中间件。
app.use(session({//提供session中间件的配置对象:三个配置属性
Secret:\’keyboard cat\’,//用于加密会话数据的密钥。 Secret 属性的值是任意字符串,用于加密会话。
resave:false, //修复写入方法以指示是否在每个请求上强制重新保存会话。这里设置为false 表示不强制重新保存。
saveUniinialized:true//修复写入方法以指示是否保存会话而不初始化它。我们将其设置为true 表示我们要保存未初始化的会话。
}))
在会话中存储数据
如果Express-session中间件配置成功,req还有额外的session属性,可以通过req.session访问session对象并存储用户的关键信息。
app.post(\’/api/login\’,(req,res)={
//判断用户提交的信息是否正确
if(req.body.username!==\’admin\’||req.body.password!==\’000000\’){
return res.send({status:1,msg:\’登录失败\’})
}
req.session.user=req.body //在session中保存用户信息
req.session.islogin=true //在session中保存用户的登录状态
res.send({status:0,msg:\’登录成功\’})
})
注意:您还可以在上述代码中的req.session.user中使用其他自定义属性名称或userinfo。
从会话中获取数据
您可以直接从req.session 对象检索以前保存的数据。示例代码如下。
//获取用户名的接口
app.get(\’api/用户名\'(req,res)={
//判断用户是否登录
if(!req.session.islogin){
返回res.send({status:1,msg:\’失败\’})
}
res.send({status:0,msg:\’成功\’,username:req.session.user.username})
})
清除会话
调用req.session.destory()函数会清除服务器存储的会话信息,但只是清除当前用户的会话,不会清除其他用户的会话。 (一般在退出时使用)
//注销接口
app.post(\’/api/logout\’,(req,res)={
//清除当前客户端对应的会话信息
req.session.destroy()
发送({
状态:0,
msg: \’成功注销\’
})
})
Web开发模式
了解会话身份验证限制
会话验证机制必须使用cookie 来实现。 Cookie默认不支持跨域访问,因此前端向后端接口进行跨域请求需要大量额外配置才能实现跨域会话认证。
消息:
如果您的前端需要后端接口并且不存在跨域问题,我们建议使用Session ID认证机制。如果您的前端需要跨域请求后端接口,我们不建议使用会话认证机制。我们建议使用JWT 身份验证机制。
什么是智威汤逊?
JWT(JSON Web Token)是目前最流行的跨域认证解决方案。 JWT 的工作原理
首先,客户端浏览器将帐号和密码发送到服务器。然后服务器对账号和密码进行验证,验证通过后对用户信息对象进行加密并生成token字符串。然后服务器将生成的令牌字符串发送到客户端的浏览器。客户端浏览器将令牌存储在localStorage 或SessionStorage 中。当客户端再次发起请求时,令牌通过请求头中的Suthorization字段发送到服务器。服务器将token字符串还原到用户信息对象中,并在用户身份认证成功后,为当前用户生成具体的响应内容并响应浏览器。
摘要: 用户信息以token字符串的形式存储在客户端浏览器上。服务器通过恢复token字符串来验证用户的身份。
注意:会话中的用户信息存储在服务器上,jwt 的用户信息存储在浏览器上。智威汤逊组件
JWT 通常由三部分组成:标头、有效负载和签名。三者之间用英文“.”分隔,格式如下:
header.payload.signature
JWT 的三个部分各自代表什么:
从前到后:标头、有效负载、签名。
Payload部分是实际的用户信息,是对用户信息进行加密后生成的字符串。 header和signature是与安全相关的部分,旨在保证token的安全。
如何使用智威汤逊
当客户端收到服务器返回的JWT 时,它通常将其存储在localStorage 或sessionStorage 中。之后客户端每次与服务器通信时,都需要检索这个jwt字符串进行身份认证。推荐的做法是将jwt放在http请求头的Authorization字段中,格式如下:
Authorization: 不记名令牌
身份认证
安装JWT相关包
npm 安装jsonwebtoken Express-jwt
在:
jsonwebtoken用于生成JWT字符串,并将用户信息转换为jwt字符串。 express-jwt 用于解析jwt 字符串并将其恢复为json 对象。当服务器收到jwt字符串时,需要将其解析为json对象。
导入JWT相关包
使用require() 函数导入两个JWT 相关包。
//导入用于生成1.jwt字符串的包
const jwt=require(\’jsonwebtoken\’)
//2、导入用户从客户端发送的jwt字符串,解析并还原为json对象包。
const ExpressJWT=require(\’express-jwt\’)
//高版本的express-jwt应该这样导入。
const{expressjwt:expressJWT}=require(\’express-jwt\’)
定义私钥
为了保证您的JWT字符串的安全,防止其在网络传输过程中被他人解密,您必须明确定义用于加密和解密的私钥。
在生成JWT字符串时,我们使用私钥对用户的信息进行加密,最后当我们检索加密的JWT字符串并解析JWT字符串将其恢复为JSON对象时,必须使用私钥进行解密。
//3.私钥的本质:是一个字符串
const SecretKey=\’星星1号^_^\’
登录成功后生成JWT字符串
调用jsonwebtoken包提供的Sign()方法将用户信息加密成JWT字符串并响应客户端。
//登录界面
app.post(\’/api/login\’,函数(req,res){
//登录失败时省略代码
//登录成功后,生成JWT字符串,通过token属性响应客户端。
发送({
状态:200,
message:\’登录成功\’,
//调用jwt.sign()生成jwt字符串。这三个参数是:用户信息对象、加密密钥和配置对象:您可以配置当前令牌的生存期。
token:jwt.sign({username:userinfo.username},secretKey,{expiresIn:\’30s\’})//您的身份将在30秒后过期,无法验证。
})
})
将JWT 字符串恢复为JSON 对象
每当客户端访问这些授权接口时,都必须主动通过请求头中的Authorization字段向服务器发送一个token字符串进行身份认证。
目前,服务器可以通过express-jwt中间件自动将客户端发送的token解析并还原为JSON对象。
//使用app.use()注册中间件
//expressJWT({secret:secretKey}) 是用于解析token 的中间件
//.unless({path:[/^\\/api\\//]}) 用于指定不需要权限的接口
app.use(expressJWT({secret:secretKEy}).unless({path:[/^\\/api\\//]}))
//需要改成这个方法来注册更高版本的express-jwt
app.use(expressJWT({ Secret: SecretKey,algorithms:[\’HS256\’]}).unless({path:[/^\\/api\\//]}))
///api(高版本)只需要验证token
app.use(\’/api\’,expressJWT({secret:secretKey,algorithms:[\’HS256\’]}))
扩展:
正则表达式[/^/api//]表示匹配以/api/开头的路径。
解释:
/: 正则表达式开始和结束符号。显示整个匹配模式。
^: 匹配字符串的开头。
/api/: 匹配字符/api/。
//: 匹配任意数量的/字符,因为路径中可以有多个/。
例子:
/api/users: 匹配
/api/products: 匹配
/api/v1/user: 匹配
/api/: 匹配
/foo/bar: 不匹配
消息:
此正则表达式匹配/api/之后的所有字符,因此它匹配以/api/开头的任何路径,无论路径长度如何。如果要匹配特定路径,例如/api/users,可以将正则表达式改为[/^/api/users$/]。
使用req.user获取用户信息
成功配置Express-jwt 中间件后,您可以将req.user 对象与这些授权接口一起使用来访问从JWT 字符串解析的用户信息。示例代码如下。
//这是一个允许的API接口
app.get(\’/admin/getinfo\’, function (req, res) {
//TODO_05: 使用req.user获取用户信息和data属性将用户信息发送给客户端
console.log(req.user);
发送({
状态: 200,
message: \’成功检索用户信息!\’,
data:req.auth//发送给客户端的用户信息
//data: req.user //发送给客户端的用户信息
})
})
注意:req 通常不包含用户属性。当你配置express-jwt中间件时,它会自动将解析的用户信息挂载到req.user属性中。例如,登录成功后,通过jwt.sign({username:userinfo.username}, SecretKey, {expiresIn:\’30s\’}) 加密的用户信息对象中包含用户名,解析出的用户只包含用户名属性。
记住!不要将密码加密为令牌字符串。
注意:如果使用postman测试访问该授权接口,则必须在header中添加Authorization。值为登录返回的Bearer+空格+token。您也可以将其直接添加到您的代码中。
//捆
发送({
状态: 200,
message: \’登录成功!\’,
token: tokenStr //发送给客户端的令牌字符串
})
//改成
发送({
状态: 200,
message: \’登录成功!\’,
token: \’Bearer \’+tokenStr //发送到客户端的令牌字符串。请注意Bearer 后面的空格。
})
注意:这个弹幕还显示req.user无法访问,新版本已经被req.auth取代。
结果应该是这样的: data: req.user //发送给客户端的用户信息没有返回任何数据,所以必须改为data:req.auth。
JWT解析失败后生成的捕获错误
使用express-jwt解析token字符串时,如果客户端发送的token字符串过期或者格式错误,会产生解析失败的错误,影响项目的正常权限。您可以通过Express的错误中间件捕获该错误并进行相关处理。示例代码如下。
//TODO_06: 使用全局错误处理中间件捕获JWT解析失败后产生的错误
app.use((err, req, res, next)={
//由于token解析失败而出错
if (err.name===\’UnauthorizedError\’) {
return res.send({ status: 401, message: \’无效令牌\’ })
}
//其他原因导致的错误
res.send({ status: 500, message: \’未知错误\’ })
})
完整代码:
//导入express模块
const Express=require(\’express\’)
//创建Express 的服务器实例
常量应用程序=Express()
//TODO_01: 安装并导入两个JWT 相关包:jsonwebtoken 和Express-jwt。
const jwt=require(\’jsonwebtoken\’)
//const ExpressJWT=require(\’express-jwt\’)
//高版本的express-jwt
const {expressjwt:expressJWT}=require(\’express-jwt\’)
//允许跨域资源共享
const cors=require(\’cors\’)
应用程序.use(cors())
//解析post表单数据的中间件
const bodyParser=require(\’body-parser\’)
app.use(bodyParser.urlencoded({extend: false }))
//TODO_02: 定义私钥。我们建议将您的密钥命名为SecretKey
const SecretKey=\’星号1 ^_^\’
//TODO_04:注册解析JWT 字符串并将其恢复为JSON 对象的中间件
//除非接口不需要权限
//app.use(expressJWT({ Secret: SecretKey}).unless({path:[/^\\/api\\//]}))
//高版本的express-jwt
app.use(expressJWT({ Secret: SecretKey, 算法: [\’HS256\’] }).unless({ path: [/^\\/api\\//] }))
///api只需要验证token
//app.use(\’/api\’,expressJWT({secret:secretKey,algorithms:[\’HS256\’]}))
//登录界面
app.post(\’/api/login\’, 函数(req, res) {
//将req.body 请求正文数据转储到userinfo 常量中
const userinfo=请求体
//登录失败
if (userinfo.username !==\’admin\’ || userinfo.password !==\’000000\’) {
//登录成功,生成JWT token
//const tokenStr=jwt.sign({ username: userinfo.username }, SecretKey, {expiresIn: \’30s\’ });
//将token添加到Authorization响应头中
//res.setHeader(\’授权\’, `Bearer ${tokenStr}`);
返回res.send({
状态: 400,
message: \’登录失败!\’
})
}
//登陆成功
//TODO_03: 登录成功后,调用jwt.sign()方法生成JWT字符串。并通过token属性发送给客户端
const tokenStr=jwt.sign({ username: userinfo.username }, SecretKey, {expiresIn: \’30s\’ })
//r
es.setHeader(\’Authorization\’, `Bearer ${tokenStr}`);
res.send({
status: 200,
message: \’登录成功!\’,
token: tokenStr // 要发送给客户端的 token 字符串
})
})
// 这是一个有权限的 API 接口
app.get(\’/admin/getinfo\’, function (req, res) {
// TODO_05:使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端
console.log(req.auth);
res.send({
status: 200,
message: \’获取用户信息成功!\’,
data: req.auth // 要发送给客户端的用户信息
})
})
// 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: \’未知错误\’ })
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(8888, function () {
console.log(\’Express server running at http://127.0.0.1:8888\’)
})
#以上关于前后端的身份认证(学习自用)的相关内容来源网络仅供参考,相关信息请以官方公告为准!
原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/93010.html