18910140161

Cookie和Session的区别 Koa2+Mysql+Redis实现登录逻辑

顺晟科技

2021-06-16 10:38:25

307

为什么我需要登录状态?

因为你需要识别用户是谁,不然怎么能在网站上看到个人信息?

为什么需要登录系统?

因为HTTP是无状态的,什么是无状态?

也就是说,这个请求和上一个请求没有关系,他们不认识,也没有关系。

我们的网站都是靠HTTP请求服务器获取相关数据。因为HTTP是无状态的,所以我们无法知道用户是谁。

所以我们需要其他方法来保护我们的用户数据。

当然,这种无国籍状态的好处是快速。

什么叫一直登录?

比如我登录了百度A页面,但是没有找到记录登录状态的地方。

去B页怎么保持登录状态?您想通过网址携带它吗?这不安全。你要求用户重新登录?去死吧。再见。用户体验不友好。

所以我们需要找到一个地方来存储用户的登录数据。这样可以给用户很好的用户体验。但这种状态通常是有保质期的,主要是为了安全。

为了解决这个问题,出现了Cookie。

饼干

Cookie是解决HTTP协议无状态缺陷的一种努力。

Cookie存在于浏览器端。也就是说,我们可以存储我们的用户信息。通常,Cookie基于从服务器发送的响应的标题字段信息,称为设置Cookie,

告诉浏览器保存Cookie。下次发送请求时,Cookie值将自动添加到请求消息中,然后发送出去。当然也可以自己操作Cookie。

如下图所示(来源《图解HTTP》)

饼干

图像

这样,我们就可以通过Cookie中的信息与服务器进行通信。

服务器如何配合?会话!

看来Cookie达到了让用户保持登录的效果。但是在Cookie中存储用户信息显然不是很安全。所以此时,我们需要存储一个的标识符。这个logo就像一把钥匙,复杂,看起来不规则,没有用户信息。只有我们自己的服务器才能知道用户是谁,别人无法模拟。

此时,会话出现,会话存储用户会话所需的信息。简单理解主要是存储key Session_ID,然后用这个key Session_ID查询用户信息。但是这个标识符需要存在于cookie中,所以Session机制需要cookie机制来保存标识符Session_ID。

如下图所示。

会议

这时你可能会想,这一届有什么用?生成一个复杂的标识并存储在服务器上。好像可以自己生成一个Session_ID,Mysql也可以存在!对,就是这样!

个人认为Session已经发展成一个抽象的概念,在行业内形成了解决方案。可能刚出现的时候有自己的规则,现在发展起来了。随着业务的复杂化,各大公司已经实现了自己的计划。

Session_id:无论你想做什么,都可以存在于你想存在的任何地方。

通常,服务器会将这个Session_id存储在缓存中,不会与用户信息表混合。一种是快速获取Session_id。第二,前面提到过,Session_id是有保质期的,为了安全,过一段时间就会过期,所以可以存放在缓存中。Common放在redis,memcached。mysql中也有一些情况,可能是用户数据比较多。但是它们并没有和用户信息表混在一起。

Cookie和会话的区别

Cookie和会话的区别

登录状态保存摘要

浏览器次请求网站,服务器生成Session ID。

将生成的会话标识保存到服务器存储中。

生成的会话标识通过设置cookie返回给浏览器。

当浏览器收到会话标识时,它将在下次发送请求时携带该会话标识。

服务器接收浏览器发送的会话标识,从会话存储中找到用户状态数据,并建立会话。

后续请求会将此会话标识交换为有状态会话。

登录流程图

登录流程图

实现案例(koa2 Mysql)

本案例适合对服务端有一定概念的同学哦,下面仅是核心代码。

数据库配置

步就是进行数据库配置,这里我单独配置了一个文件。

因为当项目大起来,需要对开发环境、测试环境、正式的环境的数据库进行区分。

让dbConf=null

const DEV={

数据库: '蒲公英',//数据库

user: 'root ',//用户

password: 'xxx ',//密码

port: '3306 ',//端口

host: '127.0.0.1' //服务互联网协议(互联网协议的缩写)地址

}

dbConf=DEV

module.exports=dbConf

数据库连接。

const MySQL=require(' MySQL ');

const dbConf=require(' ././config/DBConf ');

const pool=mysql.createPool({

host: dbConf.host,

user: dbConf.user,

password: dbConf.password,

database: dbConf.database,

})

让query=function(sql,values ) {

返回新承诺((解决,拒绝)={

pool.getConnection(函数(错误,连接){

if (err) {

拒绝(错误)

} else {

connection.query(sql,values,(err,row)={

if (err ) {

拒绝(错误)

} else {

解析(行)

}

connection.release()

})

}

})

})

}

module.exports={

查询,

}

路由配置

这里我也是单独抽离出了文件,让路由看起来更舒服,更加好管理。

const Router=require(' KOA-Router ');

const Router=new Router();

const Koacompose=require(' KOA-compose ');

const {login}=require('./controllers/log in’);

//加前缀

路由器。前缀('/API ');

module.exports=()={

//登录

router.post('/login ',登录);

返回Koacompose([路由器。routes(),路由器。allowedmethods()]);

}

中间件注册路由。

const routers=require('./路由器');

module.exports=(app)={

app。use(routers());

}

会话编号的生成和存储

我的会话编号生成用了koa-session2库,存储是存在存储里的,用了一个ioredis库。

配置文件。

const Redis=require(' iore dis ');

const { Store }=require(' KOA-session 2 ');

类RedisStore扩展了商店{

constructor() {

super();

这个。Redis=new Redis();

}

异步获取(sid,ctx) {

让data=等待。redis。get(` session : $ { sid } `);

返回JSON.parse(数据);

}

异步集(会话,{ sid=this.getID(24),maxAge=1000 * 60 * 60 }={},ctx) {

尝试{

控制台。日志(` session : $ { sid } `);

//使用redis set EX自动删除过期的会话

等等这个。redis。set(` session : $ { sid } `,JSON.stringify(session),' EX ',MaxAge/1000);

} catch (e) {}

返回(同suddenionosphericdisturbance)电离层的突然骚扰

}

异步销毁(sid,ctx) {

回来等等这个。redis。del(` session : $ { sid } `);

}

}

module.exports=RedisStore

入口文件(index.js)

const Koa=require(' Koa ');

常数中间件=require(' ./中间件');//中间件,目前注册了路由

const session=require(' KOA-session 2 ');//会话

const Store=require ' ./utils/store。js’);//redis

const body=require(' KOA-body ');

const app=new Koa();

//会话配置

app.use(会话({

store: new Store(),

key: 'SESSIONID ',

}));

//解析邮政参数

app。use(body());

//注册中间件

中间件(app);

const PORT=3001

//启动服务

app。倾听(PORT);

console.log(`server从端口${PORT} `)开始);

登录接口实现

这里主要是根据用户的账号密码,拿到用户信息。然后将用户用户界面设计(用户界面设计的缩写)存储到会议中,并将会话编号设置到浏览器中。代码很少,因为用了现成的库,人家都帮你做好了。

这里我没有把会话编号设置过期时间,这样用户关闭浏览器就没了。

const UserModel=require('./model/user model’);//用户表相关结构化查询语言语句

const用户模型=新用户模型();

/**

* @description:登录接口

* @param {account}账号

* @param {password}密码

* @return:登录结果

*/

异步函数登录(ctx,下一个){

//获取用户名密码得到

const {帐户,密码}=CTX。请求。身体;

//根据用户名密码获取用户信息

const userInfo=等待用户模型。getuserinfobyaccount(帐号,密码);

//生成会话编号

CTX。会话。uid=JSON。字符串(用户信息[0]).uid);

ctx.body={

" mes: "登录成功,

数据:用户信息[0].uid,

success: true,

};

};

module.exports={

登录,

};

登录之后其他的接口就可以通过这个会话编号获取到登录态。

//业务接口,获取用户所有的需求

const DemandModel=require('././model/DemandMoDEL’);

const DemandMoDEL=new DemandMoDEL();

const short id=require(' js-short id ');

常量存储=要求('././utils/store。js’);

const redis=new Store();

异步功能选择用户需求(ctx,下一步){

//判断用户是否登录,获取甜饼干里的便会失效

CTX。饼干。get(' SESSIONID ');

if(!SESSIONID) {

console.log('没有携带SESSIONID,去登录吧~');

返回错误的

}

//如果有SESSIONID,就去存储里拿数据

const redis data=wait redis。get(SESSIONID);

if(!redisData) {

console.log('SESSIONID已经过期,去登录吧~');

返回错误的

}

if (redisData redisData.uid) {

控制台。日志(` 0登录了,uid为$ { redis数据。uid } `);

}

const uid=JSON。解析(RedIsdata。uid);

//根据会议里的用户界面设计(用户界面设计的缩写)处理业务逻辑

const data=等待需求模型。selectdemandbyuid(uid);

console.log(数据);

ctx.body={

mes: ' ',

数据,

success: true,

};

};

module.exports={

选择用户需求,

}

坑点注意注意

1、注意跨域问题

2、处理选择多发预检测问题

app.use(async (ctx,next)={

CTX。设置('访问控制-允许-原始',' http://测试。薛。com ');

' ctx.set('访问控制-允许-凭证,true);

' ctx.set('访问控制-允许-标题','内容类型');

设置('访问控制允许方法','选项,获取,头,放,后,删除,补丁');

//这个响应头的意义在于,设置一个相对时间,在该非简单请求在服务器端通过检验的那一刻起,

//当流逝的时间的毫秒数不足访问控制更大年龄时,就不需要再进行预检,可以直接发送一次请求。

' ctx.set('访问控制-更大年龄',3600 * 24);

if (ctx.method=='OPTIONS') {

ctx.body=200

} else {

wait next();

}

});

3、允许携带甜饼干

发请求的时候设置这个参数如果:为真,请求才能携带甜饼干

axios({

URL : ' http://测试。徐。com :3001/API/登录',

方法: '发布,

数据: {

这个账户,

密码:这个.密码,

},

如果:为真,//允许设置凭证

}).然后(res={

控制台。日志(资源数据);

if (res.data.success) {

这个$router.push({

路径: '/索引'

})

}

})

相关文章
我们已经准备好了,你呢?
2024我们与您携手共赢,为您的企业形象保驾护航