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

前言

最近的新项目中,用户登录需要采用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搭建单页客户端应用,后台用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推过来了,但整体出了问题,提示如下:

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();

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

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信息,这显然不是我们想要的效果:

在查阅了一段时间后了解到,客户端是会默认携带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();

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

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信息,效果如下图:

最后

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

(0)

相关推荐

  • ASP.NET MVC结合JavaScript登录、校验和加密

    最近闲来无事给自己写了家庭财务收支管理系统,也就包含支出管理,收入管理和一些统计功能. 先说登录模块,因为涉及GET和POST请求,这些东西都是能被监控和抓取的所以就考虑这使用RSA加密解密方式传输用户名和密码参数,页面JS如下: /*需要引入三个JS文件,BigInt.js.RSA.js和Barrett.js,用到cookie则需要引入jquery.cookie.js文件*/ //与后台交互获取公钥 function getPublicKey() { var pubKey = ''; if (

  • 原生js验证简洁注册登录页面

    序 一个以js验证表单的简洁的注册登录页面,不多说直接上图 效果 主要文件 完整代码 1 sign_up.html 注册表单 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>sign-up</title> <link rel="stylesheet" href="css

  • Servlet+JavaBean+JSP打造Java Web注册与登录功能

    采用Java Web所实现的MVC结构图如下,其中控制器部分采用Servlet来实现,模型部分采用JavaBean来实现,而大部分的视图采用Jsp页面来实现. 思想基础 JSP+JavaBean两层结构工作原理应该是比较熟悉的,也比较好理解. 但是有一点必须要清楚就是用户通过浏览器来发送网页的请求,此请求到达服务器后在服务器端查找对应的网页,如果是首次请求(第二次就不用解释执行了),对于JSP来说要生成Servlet,然后通过Servlet引擎来执行 Servlet,把调用JavaBean的结果

  • JavaWeb实现用户登录注册功能实例代码(基于Servlet+JSP+JavaBean模式)

    下面通过通过图文并茂的方式给大家介绍JavaWeb实现用户登录注册功能实例代码,一起看看吧. 一.Servlet+JSP+JavaBean开发模式(MVC)介绍 Servlet+JSP+JavaBean模式(MVC)适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp负责数据显示,javabean负责封装数据. Servlet+JSP+JavaBean模式程序各个模块之间层次清晰,web开发推荐采用此种模式. 这里以一个最常用的用户登录注册程序来讲解Servlet+JS

  • 拦截JSP页面,校验是否已登录详解及实现代码

    拦截JSP页面,校验是否已登录 最近使用struts2做项目,需要拦截JSP以校验是否已经登,但struts2的过滤器链只能过滤action.后来发现在web.xml里可以直接增加javax.servlet.Filter实现类的过滤器,可以过滤JSP.请看代码 Filter实现类 <span style="font-size:18px;">package com.common; import java.io.IOException; import javax.servlet

  • js实现登录注册框手机号和验证码校验(前端部分)

    开发的很多场景中都会用到手机号的校验和验证码的校验,具体实现如下 <div> <input type="text" name="phonenumber" placeholder="请输入您的手机号码" /> <input type="text" name="verify" placeholder="请输入验证码" /><input type=&q

  • JavaScript 完成注册页面表单校验的实例

    1.步骤分析 第一步:确定事件(onsubmit)并为其绑定一个函数 第二步:书写这个函数(获取用户输入的数据<获取数据时需要在指定位置定义一个 id>) 第三步:对用户输入的数据进行判断 第四步:数据合法(让表单提交) 第五步:数据非法(给出错误提示信息,不让表单提交) 问题:如何控制表单提交? 关于事件 onsubmit:一般用于表单提交的位置,那么需要在定义函数的时候给出一个 返回值. onsubmit = return checkForm() 2.完成注册页面表单校验 <!DOC

  • js制作带有遮罩弹出层实现登录注册表单特效代码分享

    本文实例讲述了js制作带有遮罩弹出层实现登录注册表单代码特效代码.分享给大家供大家参考.具体如下: 运行效果图:                     ----------------------查看效果   源码下载----------------------- 小提示:浏览器中如果不能正常运行,可以尝试切换浏览模式. jquery制作的带有遮罩弹出层实现登录注册等表单的特效源码,是一段实现了点击后在原始页面上弹出想用页面的代码.  为大家分享的js制作带有遮罩弹出层实现登录注册表单代码特效

  • JavaScript注册时密码强度校验代码

    密码强度有4个状态,分别如下图. 无密码状态 密码低级状态 密码中级状态 密码高级状态 实现的代码主要如下: HTML代码 <input name="password" type="PassWord" onKeyUp="CheckIntensity(this.value)"> <table border="0" cellpadding="0" cellspacing="0&quo

  • js简单实现用户注册信息的校验代码

    register.html 复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta htt

随机推荐