Angular.js与node.js项目里用cookie校验账户登录详解


Posted in Javascript onFebruary 22, 2017

前言

最近的新项目中,用户登录需要采用cookie来记住用户,校验身份。所以本文就把实现的过程总结出来分享给大家,需要的朋友们可以参考学习。

在header中携带authId登录

在之前老的项目里,没有采用cookie来记录用户登录状态,而是在请求的header中携带一个身份标识来校验,大致方案如下:

  1. 客户端使用post请求提交user、password给服务端进行登录操作;
  2. 服务端校验用户是否合法,如果合法将产生一个唯一的身份标识authId,返回给客户端,客户端将此authId存放本地(如localStorage);
  3. 客户端在每次需要校验身份的请求中,往header中加入这个authId;
  4. 服务端检测当前的authId是否有效,有效则表示当前用户合法,允许操作;
  5. 客户端用户登出的时候,发送一个delete请求,告诉服务端用户注销,同时删除本地的authId信息;
  6. 服务端收到注销请求后,删除当前的authId数据。

上面的方案,如果其他客户端知道了这个authId后,可以在其他客户端模拟身份,不安全,因此弃用。

用cookie记住用户

新项目中,将采用此文即将介绍的方案?利用cookie来记住用户。主要流程是:

  1. 客户端使用post请求提交user、password给服务端进行登录操作;
  2. 服务端校验用户是否合法,如果合法将产生一个唯一的身份标识authId,以cookie的形式返回给客户端;
  3. 客户端再次请求服务端时,会携带此前已经拿到的cookie给服务端,服务端校验是否合法,合法则可以继续操作;
  4. 客户端用户登出的时候,发送一个delete请求,告诉服务端用户注销,服务端删除登录标识。

     整个过程可以用下面这张图简单表示:

Angular.js与node.js项目里用cookie校验账户登录详解

前台用angular搭建单页客户端应用,后台用node搭建服务器,数据存放在mongodb中,这三个技术及cookie基础知识本文不做介绍,感兴趣的同学可以自行了解。

以下的代码都是最简单的get/post请求,但也是最核心的部分,其他有关登录的繁琐操作,感兴趣的同学可以自行补充。

从开始?>结束,遇到的问题

首先,我用的是最基础的post请求,服务端也只是简单的返回数据,部分简单但比较核心的代码如下:

// client
$http({
 method   : 'POST',
 url   : 'http://127.0.0.1:8888/rest/user',
 data   : {name: 'xxx',password:'***'}
 }).success(function (data) {
 console.log('login success,data is:'+data);
}).error(function (data) {
 console.log('login error');
}).then(function () {
 console.log(arguments);
});

// server
var cookie = "authId=" + authId;
res.setHeader('Content-Type', 'application/json;charset=utf-8');
res.setHeader('Set-Cookie', cookie + ';Max-Age=3600;HttpOnly=false;Path=/;');
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end();

查看chrome调试,发现虽然服务端的cookie推过来了,但整体出了问题,提示如下:

Angular.js与node.js项目里用cookie校验账户登录详解

XMLHttpRequest cannot load http://127.0.0.1:8888/rest/user. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:62427' is therefore not allowed access.

分析问题后,发现原因是来自客户端的请求不能跨域访问服务端的请求,请求的资源header中没有携带允许跨越请求的信息。根据这个提示,我们把服务端的代码稍加改进后,如下:

// server
var cookie = "authId=" + authId;
res.setHeader('Content-Type', 'application/json;charset=utf-8');
res.setHeader('Set-Cookie', cookie + ';Max-Age=3600;HttpOnly=false;Path=/;');

// 添加允许跨越的头信息
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');

res.writeHead(200, {'Content-Type': 'text/plain'});
res.end();

解释下上面代码什么意思,第一句主要是允许来自任何域的请求访问,第二句是允许哪些类型的请求访问。加上后再次运行,提示如下:

Angular.js与node.js项目里用cookie校验账户登录详解XMLHttpRequest

cannot load http://127.0.0.1:8888/rest/user. Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

原因是来自客户端的请求中,Content-Type头字段,在服务端的响应信息的头中,没有携带,再次修改代码如下:

// server
var cookie = "authId=" + authId;
res.setHeader('Content-Type', 'application/json;charset=utf-8');
res.setHeader('Set-Cookie', cookie + ';Max-Age=3600;HttpOnly=false;Path=/;');

// 添加允许跨越的头信息
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');

// 添加支持Content-Type允许的头信息
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

res.writeHead(200, {'Content-Type': 'text/plain'});
res.end();

再次运行代码,发现没有错误提示,但是当我们再次请求服务器时,发现客户端的请求并没有携带cookie信息,这显然不是我们想要的效果:

Angular.js与node.js项目里用cookie校验账户登录详解

在查阅了一段时间后了解到,客户端是会默认携带cookie给服务端的,但是当客户端的请求是跨域请求时,由于跨域请求本身就有风险,而携带给cookie同样有风险。

因此在进行跨域访问时,客户端不会将服务端返回的cookie携带。此时,我们需要同时在客户端和服务端都设置“withCredentials”为true,代码如下:

// client
$http({
 method   : 'POST',
 url   : 'http://127.0.0.1:8888/rest/user',
 withCredentials: true
 data   : {name: 'xxx',password:'***'}
 }).success(function (data) {
 console.log('login success,data is:'+data);
}).error(function (data) {
 console.log('login error');
}).then(function () {
 console.log(arguments);
});

// server
var cookie = "authId=" + authId;
res.setHeader('Content-Type', 'application/json;charset=utf-8');
res.setHeader('Set-Cookie', cookie + ';Max-Age=3600;HttpOnly=false;Path=/;');

// 添加允许跨越的头信息
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');

// 添加支持Content-Type允许的头信息
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

// 设置已携带凭证为true
//res.setHeader('Access-Control-Allow-Credentials', true);

res.writeHead(200, {'Content-Type': 'text/plain'});
res.end();

运行后,发现又有错误提示,如下:

Angular.js与node.js项目里用cookie校验账户登录详解

XMLHttpRequest cannot load http://127.0.0.1:8888/rest/user. Response to preflight request doesn't pass access control check: A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. Origin 'http://localhost:62427' is therefore not allowed access.

分析错误后发现,原因是当设置了已携带凭证参数为true时,允许跨域请求的源不能设置为泛型的“*”,因此我们再次修改代码如下:(最终代码)

// client
$http({
 method   : 'POST',
 url   : 'http://127.0.0.1:8888/rest/user',
 withCredentials: true
 data   : {name: 'xxx',password:'***'}
 }).success(function (data) {
 console.log('login success,data is:'+data);
}).error(function (data) {
 console.log('login error');
}).then(function () {
 console.log(arguments);
});

// server
var cookie = "authId=" + authId;
res.setHeader('Content-Type', 'application/json;charset=utf-8');
res.setHeader('Set-Cookie', cookie + ';Max-Age=3600;HttpOnly=false;Path=/;');

// 添加允许跨越的头信息
// res.setHeader('Access-Control-Allow-Origin', '*');
// 用当前的客户端origin来取代泛型的“*”
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:62427');

res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');

// 添加支持Content-Type允许的头信息
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

// 设置已携带凭证为true
res.setHeader('Access-Control-Allow-Credentials', true);

res.writeHead(200, {'Content-Type': 'text/plain'});
res.end();

此时,第一次请求服务端时,服务端返回cookie信息,以后每次客户端请求服务端,客户端的header中都会携带cookie信息,效果如下图:

Angular.js与node.js项目里用cookie校验账户登录详解

最后

以上就是在使用cookie记住用户身份时遇到的一些问题及简单解决方法,一般在angular应用中,可能使用较多的是resoure进行http通信,此时只要在GET/POST/PUT/DELETE等请求的参数中,将“withCredentials”设置为true即可。希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
Firefox window.close()的使用注意事项
Apr 11 Javascript
ExtJS 2.0实用简明教程 之Ext类库简介
Apr 29 Javascript
Javascript全局变量var与不var的区别深入解析
Dec 09 Javascript
jQuery根据用户电脑是mac还是pc加载对应样式的方法
Jun 26 Javascript
使用JavaScript脚本无法直接改变Asp.net中Checkbox控件的Enable属性的解决方法
Sep 16 Javascript
深入剖析JavaScript中的函数currying柯里化
Apr 29 Javascript
jQuery联动日历的实例解析
Dec 02 Javascript
JavaScript函数表达式详解及实例
May 05 Javascript
JS与HTML结合实现流程进度展示条思路详解
Sep 03 Javascript
node.js实现的装饰者模式示例
Sep 06 Javascript
移动端H5页面返回并刷新页面(BFcache)的方法
Nov 06 Javascript
vue实现点击出现操作弹出框的示例
Nov 05 Javascript
js实现无缝滚动图
Feb 22 #Javascript
JavaScript实现二分查找实例代码
Feb 22 #Javascript
浅谈jquery拼接字符串效率比较高的方法
Feb 22 #Javascript
微信小程序  TLS 版本必须大于等于1.2问题解决
Feb 22 #Javascript
原生JS实现幻灯片
Feb 22 #Javascript
微信小程序 解析网页内容详解及实例
Feb 22 #Javascript
从零学习node.js之简易的网络爬虫(四)
Feb 22 #Javascript
You might like
深入PHP操作MongoDB的技术总结
2013/06/02 PHP
让ThinkPHP支持大小写url地址访问的方法
2014/10/31 PHP
php简单操作mysql数据库的类
2015/04/16 PHP
php实现将HTML页面转换成word并且保存的方法
2016/10/14 PHP
php获取指定数量随机字符串的方法
2017/02/06 PHP
PHP检查网站是否宕机的方法示例
2017/07/24 PHP
解决php extension 加载顺序问题
2019/08/16 PHP
jQuery 注意事项 与原因分析
2009/04/24 Javascript
JavaScript 异步调用框架 (Part 5 - 链式实现)
2009/08/04 Javascript
一些mootools的学习资源
2010/02/07 Javascript
JavaScript实现动态添加,删除行的方法实例详解
2015/07/02 Javascript
jQuery实用技巧必备(中)
2015/11/03 Javascript
基于Jquery easyui 选中特定的tab
2015/11/17 Javascript
javascript实现简单加载随机色方块
2015/12/25 Javascript
AngularJS ng-blur 指令详解及简单实例
2016/07/30 Javascript
jQuery动态添加li标签并添加属性和绑定事件方法
2018/02/24 jQuery
element vue Array数组和Map对象的添加与删除操作
2018/11/14 Javascript
Javascript数组及类数组相关原理详解
2020/10/29 Javascript
[16:43]Heroes19_剃刀(完美)
2014/10/31 DOTA
使用python装饰器验证配置文件示例
2014/02/24 Python
python之wxPython应用实例
2014/09/28 Python
python中列表元素连接方法join用法实例
2015/04/07 Python
Python用threading实现多线程详解
2017/02/03 Python
Python标准库shutil用法实例详解
2018/08/13 Python
浅谈python 读excel数值为浮点型的问题
2018/12/25 Python
Matplotlib animation模块实现动态图
2021/02/25 Python
美国玛丽莎收藏奢华时尚商店:Marissa Collections
2016/11/21 全球购物
申请任职学生会干部自荐书范文
2014/02/13 职场文书
保险公司晨会主持词
2014/03/22 职场文书
宣传工作经验材料
2014/06/02 职场文书
学校副校长四风对照检查材料整改措施
2014/09/25 职场文书
夫妻婚内购房协议书
2014/10/05 职场文书
见习报告怎么写
2014/10/31 职场文书
2014年招商引资工作总结
2014/11/22 职场文书
PHP策略模式写法
2021/04/01 PHP
Python软件包安装的三种常见方法
2022/07/07 Python