最近的新项目中,用户登录需要采用cookie来记住用户,校验身份。
在之前老的项目里,没有采用cookie来记录用户登录状态,而是在请求的header中携带一个身份标识来校验,大致方案如下:
客户端使用post请求提交user、password给服务端进行登录操作;
服务端校验用户是否合法,如果合法将产生一个唯一的身份标识authId,返回给客户端,客户端将此authId存放本地(如localStorage);
客户端在每次需要校验身份的请求中,往header中加入这个authId;
服务端检测当前的authId是否有效,有效则表示当前用户合法,允许操作;
客户端用户登出的时候,发送一个delete请求,告诉服务端用户注销,同时删除本地的authId信息;
服务端收到注销请求后,删除当前的authId数据。
上面的方案,如果其他客户端知道了这个authId后,可以在其他客户端模拟身份,不安全,因此弃用。
用cookie记住用户 新项目中,将采用此文即将介绍的方案–利用cookie来记住用户。主要流程是:
客户端使用post请求提交user、password给服务端进行登录操作;
服务端校验用户是否合法,如果合法将产生一个唯一的身份标识authId,以cookie的形式返回给客户端;
客户端再次请求服务端时,会携带此前已经拿到的cookie给服务端,服务端校验是否合法,合法则可以继续操作;
客户端用户登出的时候,发送一个delete请求,告诉服务端用户注销,服务端删除登录标识。 整个过程可以用下面这张图简单表示:
前台用angular搭建单页客户端应用,后台用node搭建服务器,数据存放在mongodb中,这三个技术及cookie基础知识本文不做介绍,感兴趣的同学可以自行了解。 以下的代码都是最简单的get/post请求,但也是最核心的部分,其他有关登录的繁琐操作,感兴趣的同学可以自行补充。
从开始–>结束,遇到的问题 首先,我用的是最基础的post请求,服务端也只是简单的返回数据,部分简单但比较核心的代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$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);
});
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推过来了,但整体出了问题,提示如下:
1
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中没有携带允许跨越请求的信息。根据这个提示,我们把服务端的代码稍加改进后,如下:1
2
3
4
5
6
7
8
9
10
11
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();
解释下上面代码什么意思,第一句主要是允许来自任何域的请求访问,第二句是允许哪些类型的请求访问。加上后再次运行,提示如下:
1
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头字段,在服务端的响应信息的头中,没有携带,再次修改代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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信息,这显然不是我们想要的效果:
在查阅了一段时间后了解到,客户端是会默认携带cookie给服务端的,但是当客户端的请求是跨域请求时,由于跨域请求本身就有风险,而携带给cookie同样有风险。 因此在进行跨域访问时,客户端不会将服务端返回的cookie携带。此时,我们需要同时在客户端和服务端都设置“withCredentials”为true,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
$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 );
});
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.setHeader('Access-Control-Allow-Headers' , 'Content-Type' );
res.writeHead(200 , {'Content-Type' : 'text/plain' });
res.end();
运行后,发现又有错误提示,如下:
1
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时,允许跨域请求的源不能设置为泛型的“*”,因此我们再次修改代码如下:(最终代码 )1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$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 );
});
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' , 'http://localhost:62427' );
res.setHeader('Access-Control-Allow-Methods' , 'GET,PUT,POST,DELETE,OPTIONS' );
res.setHeader('Access-Control-Allow-Headers' , 'Content-Type' );
res.setHeader('Access-Control-Allow-Credentials' , true );
res.writeHead(200 , {'Content-Type' : 'text/plain' });
res.end();
此时,第一次请求服务端时,服务端返回cookie信息,以后每次客户端请求服务端,客户端的header中都会携带cookie信息,效果如下图:
最后 以上就是在使用cookie记住用户身份时遇到的一些问题及简单解决方法,一般在angular应用中,可能使用较多的是resoure进行http通信,此时只要在GET/POST/PUT/DELETE等请求的参数中,将“withCredentials”设置为true即可。
☜☜☜ wechat 『『『 reward 点击扫码打赏 ~~~ ^_^ 』』』alipay ☞☞☞