Cookie 会话身份验证是如何工作的?
在 Web 应用程序中,Cookie-Session 是一种标准的身份验证方法。饼干,也被称为“sweet cookies”。类型为“小文本文件”,是指一些网站为了识别用户身份而存储在客户端的数据。Session的主要功能是通过服务器记录用户的状态。
在典型的在线购物场景中,用户浏览多个页面并将一些商品添加到购物车。尽管如此,系统并不知道是哪个用户进行了操作,因为HTTP协议是无状态的,所以服务端需要为不同的用户创建相应的Session来识别和跟踪这个用户。在服务器端保存Session的方式有很多种,比如保存到内存、数据库或者文件中。
我们看一下Cookie-Session的认证过程:
这是一个典型的 HTTP 客户端(浏览器)和 HTTP 服务器对话,服务器运行在同一台计算机(本地主机)上,包含以下步骤。
- 用户访问登录页面后,输入相应的用户名和密码进行登录操作。
- 在客户端发起登录请求。一般在浏览器环境下,可以通过AJAX或者Form发起登录请求。
- 当服务器通过认证时,将为认证成功的用户创建一个Session,并存储Session信息。之后服务器会通过HTTP响应头来设置SessionId,即HTTP响应头中会包含 的响应头信息Set-Cookie。如果认证失败,则提示用户进行相关操作。
- 认证成功后,用户访问页面获取用户信息,此时客户端会在HTTP请求头中携带cookie信息。
- 服务端接收到客户端发起的请求,获取cookie中存储的SessionId来验证用户身份,验证通过后返回相应信息。
下面我将使用Koa来介绍Cookie-Session的认证过程。首先我们来定义首页的路由:
// router.js
路由器。get ( "/" , async ( ctx ) => {
if (ctx.session.user ) { const { userName } = ctx.session.user ; ctx.body = ` < h1 > Cookie /Session Authentication</h1> 你好, ${userName} <div> <a href="./getUserInfo">用户信息</a> <a href="./logout">注销</a> </div> ` ; } else { ctx.
“登录”);
}
});
在上面的代码中,我们首先会判断当前用户是否已经登录到web应用中。如果已登录,将显示与当前用户对应的问候消息。如果没有登录,会跳转到登录页面,所以我们需要注册登录页面的路由:
// router.js
路由器。get ( "/login" , ( ctx ) => {
if (ctx.session.user ) { ctx.redirect ( " /" ); } else { ctx.body = ` <form method = " post" action="/ login"> <p><label>用户名</label><input type="text" name="username" /></p> <p><label>密码</label><input type="password" name="密码" /></p> <button type="
当未登录的用户在浏览器中访问
http://localhost:3000/login地址时,会显示如下用户登录表单:
当用户输入用户名和密码并点击登录按钮时,浏览器会发起POST请求并调用/loginAPI。对应的/login路由处理逻辑如下:
// router.js
路由器。post ( "/login" , async (ctx) => {
try {
const loginData = ctx.request . body ; const
{ username, password } = loginData;
if (username === "bytefer" && password === "123 " ) {
ctx. session . user = { userName : "bytefer" , userId : "007" };
ctx. response . redirect ( "/"
}否则{
ctx。body = {代码:0,消息:“登录失败!” };
}
} catch (err) {
throw new Error ( "登录错误" );
}
});
在上面的代码中,我们使用ctx.request.body获取用户输入的用户名和密码,然后判断输入的用户名和密码是否完全匹配。当然,在真实的web项目中,通常会判断输入的信息与数据库user表中的信息一致。此外,为确保系统的安全,用户的密码将被加密或散列。如果输入的用户信息完全匹配,我们将当前登录的用户信息设置到该ctx.session.user属性并重定向到首页。并且如果用户信息不匹配,页面上会显示相应的错误信息。
我们看一下登录成功后服务器返回的HTTP响应报文:
从上图可以看出,登录成功后,服务器返回的HTTP响应报文中会包含Set-Cookie响应头。之后我们打开Chrome开发者工具的Application Tab页面,可以看到已经设置好的Cookie信息:
对于大部分的Web应用,一般都会提供一个用户信息页面,让用户可以查看当前已经登录成功的用户信息。这里我们也注册了一个/getUserInfo路由,用于显示已登录的用户信息:
// router.js
路由器。get ( "/getUserInfo" , async ( ctx ) => {
if ( ctx.session.user ) { const { userName, userId } = ctx.session.user ; ctx.body = ` < div > UserName : ${userName} , UserId: ${userId} </div> ` ; } else { ctx.body = { code : 0 , message :
“您还没有登录。” };
}
});
用户登录成功后,访问web应用中的其他路由时,会发起相应的HTTP请求,请求头中会自动携带当前用户的Cookie信息,如下图:
除了上述功能,我们还需要注册一个路由来处理用户注销操作:
// router.js
路由器。get ( "/logout" , async ( ctx ) => {
ctx.session = null ; ctx.body = ` < h1 >成功注销</h1> <a href="./login">登录</a> ` ; });
同样我们看一下注销成功后服务器返回的HTTP响应报文:
已经描述了包含在 Web 应用程序中的路由。为了应用能够正常运行,我们还需要创建一个Koa应用,并配置koa-session、koa-bodyparser等中间件。具体代码如下:
// app.js
const Koa = require ( "koa" );
const app = new Koa ();
const session = require ( "koa-session" );
const bodyParser = require ( "koa-bodyparser" );
常量端口 = 3000 ;
const router = require ( "./router.js" );
const CONFIG = {
key : "sid" ,
maxAge : 86400000 ,
httpOnly: true ,
signed : true ,
rolling : false ,
renew : false ,
sameSite : null ,
};
应用程序。键= [ “bytefer” ];
应用程序。use ( async function ( ctx, next ) { try
{ await
next ( );
} catch (err) {
ctx.status = err.status || 500 ; ctx.type
= "html" ;
CTX。body = "<p>系统错误</p>" ;
CTX。应用程序。发出(“错误”,错误,ctx);
}
});
应用程序。使用(会话(配置,应用程序));
应用程序。使用( bodyParser ());
应用程序。使用(路由器。路由())。使用(路由器。allowedMethods());
应用程序。on ( "error" , ( err, ctx ) => {
控制台. 日志(错误);
});
应用程序。listen (port, function ( ) {
console . log ( `服务器运行在 http://localhost: ${port} ` );
});
Cookie-Session的认证过程已经介绍过了,最后总结一下这种认证方式存在的一些问题。
饼干的缺陷
- 每次HTTP请求都会添加Cookies,无形中增加了流量。
- 安全性差,攻击者可以利用本地 cookie 进行欺骗和 CSRF 攻击。
- Cookie 大小限制在 4KB 左右,不足以满足复杂的存储要求。
会话缺陷
- session保存在服务器端,如果短时间内有大量用户,会影响服务器的性能。
- 可扩展性不好。当有多台服务器时,如何共享Session就会成为一个问题。也就是说当用户第一次访问服务器A,第二次请求转发给服务器B时,服务器B无从知晓其状态。
那么如何解决以上问题呢?解决方案之一是使用基于令牌的身份验证。在下一篇文章中,我将介绍JWT认证方式。有兴趣记得关注我哦。
如果需要更加全面的学好前端,也可以来参与我们的三十天学习计划,全程不涉及任何费用!这是一套免费的三十天挑战计划的课程体系,包含了html+css+云端部署的课程体系,可以通过钉钉群里学习,有问题在群里可以提问,同时每节课还安排有作业,配套有阶段项目练习和综合项目实战,目的是帮助大家夯实前端基础,轻松入门到前端行业
为帮助到一部分同学不走弯路,真正达到一线互联网大厂前端项目研发要求,首次实力宠粉,打造了《30天挑战学习计划》,内容如下:
HTML/HTML5,CSS/CSS3,JavaScript,真实企业项目开发,云服务器部署上线,从入门到精通
- PC端项目开发(1个)
- 移动WebApp开发(2个)
- 多端响应式开发(1个)
共4大完整的项目开发 !一行一行代码带领实践开发,实际企业开发怎么做我们就是怎么做。从学习一开始就进入工作状态,省得浪费时间。
从学习一开始就同步使用 Git 进行项目代码的版本的管理,Markdown 记录学习笔记,包括真实大厂项目的开发标准和设计规范,命名规范,项目代码规范,SEO优化规范
从蓝湖UI设计稿 到 PC端,移动端,多端响应式开发项目开发
- 真机调试,云服务部署上线;
- Linux环境下 的 Nginx 部署,Nginx 性能优化;
- Gzip 压缩,HTTPS 加密协议,域名服务器备案,解析;
- 企业项目域名跳转的终极解决方案,多网站、多系统部署;
- 使用 使用 Git 在线项目部署;
这些内容在《30天挑战学习计划》中每一个细节都有讲到,包含视频+图文教程+项目资料素材等。只为实力宠粉,真正一次掌握企业项目开发必备技能,不走弯路 !
过程中【不涉及】任何费用和利益,非诚勿扰 。
如果你没有添加助理老师微信,可以添加下方微信,说明要参加30天挑战学习计划,来自公众号!老师会邀请你进入学习,并给你发放相关资料。