在开发一个网站时,绝大多数数状况都会用到登陆、自动登陆功能。本篇是第一篇,用cookie和session来记录用户数据。html
你们都知道http协议是无状态的,也就是,从这个页面到另外一个页面,服务端是不知道是否为同一用户的。因此,慢慢发展成了前端用cookie保存sessionID,后端使用session进行加密及解密的一套方法。前端
在这里我稍微讲一下cookie和session是如何合做的,咱们前端人员刚接触node开发时,思惟多是须要进行转化的,可能转化须要一段时间。可是当你搞明白了之后,成为全栈工程师后,就会对整个流程了然于胸了。这里使用express来说解vue
首先,明确要记录用户,cookie里应该要存什么。注意,cookie里尽可能不要存储密码和用户名,一是有被窃取的危险,而是增长cookie的大小。(固然并非说必定不能这么作,好比把用户名和密码进行md5等方式处理,也相对会很安全)cookie里存储的是sessionID,那这个sessionID是如何跟服务端进行交互的,后面详细讲。node
用户的请求过来之后,若是在请求体中没有cookie,则建立一个新的session,而且建立一个不会重复的sessionID, 把seesionID和session在store里进行关联。(store默认是内存)ios
MemoryStore.prototype.set = function set(sessionId, session, callback) {
this.sessions[sessionId] = JSON.stringify(session)
callback && defer(callback)
}
复制代码
当setCookie的时候,把注册session中间件中的option进行处理:vue-router
具体是把option中的name, secret跟生成的sessionID进行签名加密,设置res.setHeader('Set-Cookie', header) 把加密后的结果传到客户端。数据库
这时,cookie就会随请求头到了服务端, 那么express-session就会对cookie进行解密,把sessionID解出来,而后经过这个id,就找到了store中存储的session,也就拿到session中的用户数据,好比username等(前提是在登陆时,把username存到了session上面,好比req.ression.username = user)。express
这时,服务端就知道访问者是谁了,就能够把属于这个访问者的数据返回到页面中。json
上面讲解了总体流程,虽然没有展开讲,但我理解能够帮助你们抓到总体脉络。axios
好了,下面开始展开讲代码。具体的各个key是什么含义,我就不讲了,具体可看文档。
express-session是个中间件,那么就须要把这个中间件加载到express程序中。
const session = require('express-session');
app.use(session({
secret: 'test-node-session',
resave: false,
saveUninitialized: true,
cookie: {
path: '/',
httpOnly: true,
secure: false,
// 若是不设置maxAge,则退出关闭浏览器tab,当前cookie就会过时
//maxAge: 1000 * 60
}
}));
复制代码
经过上面设置,当一个请求来了之后,若是以前页面没有对应的cookie,则产生一个新的cookie,若是有,会进行更新后从新发送到浏览器中。
固然,页面请求后就会产生这个name为connect.sid的cookie。 这时咱们进行登陆:
登陆时,经过post请求,把数据和cookie都给到了服务端。
app.post('/author/gologin', (req, res) => {
// 经过req.body拿到用户名和密码、是否自动登陆
...
// 去数据库里进行匹配,若是匹配到,说明登陆成功
....
// 这时,能够把用户名存储到session中,
req.session.user = req.body.username;
// 由于咱们是单页应用,因此返回json,让前端路由到对应的页面
res.json({
code: 0,
mesg: ''
});
})
复制代码
前端页面拿到请求的返回值,决定路由到哪一个页面。
// 使用vue
axios.post('/author/gologin', {
data: JSON.stringify(this.accountFormItem)
})
.then(res => {
console.log('登陆帐号返回数据:', JSON.stringify(res));
switch (+res.data.code) {
case 0:
this.$router.push('/go/to/path/list');
break;
case 2004:
this.inputError = true;
break;
default:
break;
}
})
.catch(err => {
console.error('登陆帐号出现错误:', err);
});
复制代码
这时,vue-router会进行导航,到了对应的页面。 当到了新的页面后,好比到了列表页,那就会涉及去访问列表的异步接口,
这里注意,应该在全部的异步请求中去判断一下用户是否存在,也就是先查一下用户是否已经登陆。好比客户端发送了一个get请求 '/get/total/list', 到了服务端,进行处理:
// node进行处理
app.get('/get/total/list', (req, res) => {
if (!req.session.user) {
return res.status(401).send({msg: 'Unauthorize'});
}
// 去数据库里查数据
...
// 把数据返回给客户端
return res.json({code: 0, data: result});
})
复制代码
在上面的代码中,有很重要的一句话,那就是若有找到req.session.user,则继续流程,若是没有找到,就返回401状态,也就是未认证,这个状态是符合http语义的。
那么服务端返回这个状态,客户端须要拿到而且路由到登陆页。这种状况通常不会出现,可是要处理,好比说有人更改了cookie的时候,就匹配不到用户数据了。
// 客户端使用axios,能够对数据的返回进行拦截,在单页的app.js页面
axios.interceptors.response.use(response => {
// 查看返回的状态
return response
},
error => {
if(error.response.status === 401) {
router.push('/login');
}
}
);
复制代码
这时页面回到了登陆页,要求用户从新登陆。
上面就是整个过程了。固然,上面仍是个比较粗略的实现,没有包含用户直接刷新页面的状况。下面稍微提一下。
好比当用户直接刷新列表页,咱们知道,单页应用只有一个html入口文件,好比默认是index.html,那么过程就是须要把index.html返回到浏览器,浏览器根据路由渲染对应的页面,而后进行页面内容的异步请求。(具体如何作到,请看我对单页应用在node中运行起来的相关文章)
这里有个体验问题: 若是用户在这时把cookie删除了,那么页面会先进行上面说的步骤,最后才返回401,而后再跳转到登陆页,会有一个很明显的闪烁过程,先把列表页加载了出来,又跳到了登陆页,用户体验很差。
那么咱们能够作些优化。
// vue 全局路由守卫
router.beforeEach((to, from, next) => {
function getRouteEach(to, next) {
if (sessionStorage.getItem('testuser')) {
// 若是url是登陆页,则路由到内容页
if (to.path === '/login') {
return next({path: '/to/the/path/list'});
}
next();
} else {
// 若是发现没有登陆,则去请求一次登陆状态
axios.get('/api/getsid')
.then(res => {
// 服务端有登陆状态
if (res.data.code === 0) {
sessionStorage.setItem('testuser', res.data.data.sessionName);
// 路由到对应页面
return getRouteEach(to, next);
} else if (to.path !== '/login') {
// 若是确认没有登陆,则跳到登陆页面
return next({path:'/login'});
}
next();
})
.catch(e => {
console.error('查看sid失败:', e);
})
}
}
getRouteEach(to, next);
});
复制代码
你们都知道,很长一段时间内,你们都用这种方式来处理,可是这里有个问题,就是在防csrf攻击(跨站请求伪造)的时候,就容易防不住了。固然能够用好比双cookie认证等方式增强,可是下一篇我要讲的token方式更为方便。<<从单页应用看node的token>> 欢迎你们继续关注~~